REN

Ph.D. in Computer Science at Rutgers University

Talking about C pointer - Void Pointer and Generic Programming in C

In most Object-oriented Languages like Java, C++, Generic Programming is a language feature that allows programmers to abstract types from data structures and algoritms. In C language, we could also implement generic programming and polymorphism.

Simple Method - Using Macro

A simple method to achieve generic programming is to use macro. Let's see the following example:

/* test.c */
#include <stdio.h>

#define SWAP(x,y) {\
y=x+y;\
x=y-x;\
y=y-x;\
}

int main() {
	int a = 5, b = 3;
	double c = 1.0, d = 2.0;
	char e = 'e', f = 'f';
	SWAP(a, b);
	SWAP(c, d);
	SWAP(e, f);
	printf("now a = %d, b = %d\n", a, b);
	printf("now c = %f, d = %f\n", c, d);
	printf("now e = %c, f = %c\n", e, f);
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

now a = 3, b = 5
now c = 2.000000, d = 1.000000
now e = f, f = e

The macro defined function SWAP above is a small example of generic programming in C, as it could be in C++ using Template. This method is sample, and macro defined function has advantage in efficiency for no overhead in procedure call. However it has limitations. If the function is very complex, the macro would be unfriendly to maintain or the function is even impossible to be written in macro.


Pointer of Different Types

Now, let's look at a special type of pointer in C - void pointer. As we know, a pointer itself is a variable so it has types. The size of a pointer is 4 Byte in a 32-bit machine while 8 Byte in a 64-bit machine. However, for a different types of pointers, even the address they pointing to is same, the data are different. How, let's look at a small example.

/* test.c */
#include <stdio.h>

int main() {
	char *s = "abcdefg";
	char *a = s;
	int *b = (int*)s;
	double *c = (double*)s;
	printf("a = %c, b = 0x%x, c = %f\n", *a, *b, *c);
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

a = a, b = 0x64636261, c = 0.000000

Void Pointer

Void pointer could cast to any type of pointer and vice versa. Thus, for a function have a pointer parameter, if we want this function to have generic feature on pointers, we could use void pointer. Here's an example.

/* test.c */
#include <stdio.h>

void swap(void *a, void *b, int size) {
	char buf[50];
	memcpy(buf, a, size);
	memcpy(a, b, size);
	memcpy(b, buf, size);
}

int main() {
	int a = 5, b = 3;
	double c = 1.0, d = 2.0;
	char e = 'e', f = 'f';
	swap(&a, &b, 4);
	swap(&c, &d, 8);
	swap(&e, &f, 1);
	printf("now a = %d, b = %d\n", a, b);
	printf("now c = %f, d = %f\n", c, d);
	printf("now e = %c, f = %c\n", e, f);
	return 0;
}

Let's compile this program in GCC:

gcc -m32 -o test test.c

The result of those codes are:

now a = 3, b = 5
now c = 2.000000, d = 1.000000
now e = f, f = e

The void pointer could very tricky since it doesn't have type. Programmers should be very careful when do type casting using void pointer. It could be OK if a double pointer cast to an int pointer. But it could cause illegal access when a char pointer is cast to a double pointer, and dereferenced to be a double variable.

Conclusion

C Language emerged earlier than most of our modern high-level programming language especially for object-oriented programming languages. Some people criticizes type systems for C, I partially agree ... but C built the foundation of mordern computer systems, it's non-replacable. Afterall, C is build for low-level systems. We still have some methods to achieve generic programming and this post shows two; We could also use Union to achieve this. In the next post, the last one of C pointer series, I'll talk about a more advanced pointer - function pointer and we could use void pointer and function pointer to achieve polymorphism and even simple OOP mechaism in C!