Examples of Functions Using void*
Learn about multiple functions from the C standard library which rely on void pointers.
We'll cover the following
Introduction
The C standard library offers us a lot of handy functions with void
pointers. Since these functions need to work on any possible data type, they can’t be limited to int
or char
. They need to accept or return any data type, and they use void*
to accomplish this.
We’ll look at a few examples of such functions. We already saw most of them, but we didn’t know they used void
pointers.
malloc
and calloc
The prototypes are as follows:
void* malloc(size_t size)
void* calloc (size_t num, size_t size);
Since malloc
and calloc
can allocate a block of any data type, by specifying the size, they must return void*
. We then implicitly cast the returned pointer to our desired data type when writing constructs like the following:
int* intPtr = malloc(sizeof(int));
Sometimes it may be a good idea to explicitly cast the return value of malloc
or calloc
to the desired data type.
int* intPtr = (int*)malloc(sizeof(int));
Note: C++ compilers require explicit type casting. They don’t allow the implicit conversion we saw in the first code example.
memcpy
and memset
The prototypes are:
void* memset(void* ptr, int value, size_t num);
void* memcpy(void* destination, const void* source, size_t num);
Let us focus on the arguments.
memset
initialized each byte from a memory block to a particularvalue
. Therefore, it must accept avoid*
.memcpy
copies a number of bytes from asource
buffer to adestination
buffer. It doesn’t care what the data types are. It just copies them byte by byte. Therefore, it must accept the arguments asvoid*
.
We may ask how memset
performs the initialization since we know we can’t dereference void
pointers. It’s a good question, and we’ll soon learn how to do it.
We’ll see how memset
and memcpy
work internally in a future lesson, where we’ll learn about reinterpreting memory. It’s going to be an exciting lesson!
The qsort
function
Let’s look again at qsort
. We used it twice before to sort some data, and we said to ignore the void*
and the typecasting parts of the code.
This time, we’ll understand how qsort
works in depth. Let’s build another program, to sort an array of integers. Nothing fancy, as we want to focus on the internals of qsort
.
Recall that the prototype of qsort
is:
void qsort(void* base, size_t nitems, size_t size, int (*comparatorFunc)(const void *, const void*))
- It takes the array as a
void*
to accept any data type. - The next argument is the number of elements to sort.
- After that, we must specify the size of each element.
- Finally, it accepts a comparator function. Since we must compare different data types using different rules, we need to specify the rule for our data type.
qsort
allows us to specify this using a comparator function.- Notice that the comparator is a pointer to a function.
comparatorFunc
is a pointer to a function taking two constantvoid
pointers as arguments and returningint
.- Since the array elements are unknown to
qsort
, the comparator takes the elements to compare asvoid*
. - If the two elements are
a
andb
, the comparator must return:< 0
ifa
<b
> 0
ifa
>b
= 0
ifa
=b
- When sorting integers, a common shortcut is to return
a - b
, which produces a value that respects the above conditions.
We call qsort
in line 30 with the comparator defined in line 6. We want to sort NUM_ELEMS
elements, where each element has the size of an int
.
The interesting aspect is to see how we wrote the comparator.
- The comparator passes two pointers to two elements from the array.
- The pointers are
void*
, so we must convert them back toint*
(lines 8–9). We use a typecast(int*)
. - Next, we return the appropriate value using the shortcut
*elem1 - *elem2
(line 11). This trick doesn’t work correctly for floating point elements. In this case, compare the elements explicitly and return the correct value.
Get hands-on with 1400+ tech skills courses.