You can absolutely safely pass a pointer to any object to a function like this using void *
. The problem, as others have pointed out, is that you have to do something with that object once you're in that function, and you'll need to know the type to do that, so even if you don't need the type to pass a pointer to the array, you're going to need it in some fashion to get any actual work done.
You can do it with C11 generics, as another answer states, but another possibility is to pass not only a pointer to the array, but a pointer also to the code you want to execute on it, using a function pointer.
For instance, the following code will add two to each element of any numeric array (I've implemented it only for int
, long
and double
, but it's trivial to implement for the remaining standard numeric types). The add_two()
function itself never has any idea what type of array it's getting. But to achieve this, you have to also pass it a pointer to a function which does know:
#include <stdio.h>
void add_two(void * array, size_t sz, void (*add_func)(void *, size_t));
void add_two_int(void * array, size_t idx);
void add_two_long(void * array, size_t idx);
void add_two_double(void * array, size_t idx);
int main(void)
{
int n[] = {1, 2, 3, 4, 5};
add_two(n, 5, add_two_int);
for ( size_t i = 0; i < 5; ++i ) {
printf("%zu: %d\n", i, n[i]);
}
long l[] = {1L, 2L, 3L, 4L, 5L};
add_two(l, 5, add_two_long);
for ( size_t i = 0; i < 5; ++i ) {
printf("%zu: %ld\n", i, l[i]);
}
double d[] = {1.0, 2.0, 3.0, 4.0, 5.0};
add_two(d, 5, add_two_double);
for ( size_t i = 0; i < 5; ++i ) {
printf("%zu: %f\n", i, d[i]);
}
return 0;
}
void add_two(void * array, size_t sz, void (*add_func)(void *, size_t))
{
for ( size_t i = 0; i < sz; ++i ) {
add_func(array, i);
}
}
void add_two_int(void * array, size_t idx)
{
int * n = array;
n[idx] += 2;
}
void add_two_long(void * array, size_t idx)
{
long * l = array;
l[idx] += 2;
}
void add_two_double(void * array, size_t idx)
{
double * d = array;
d[idx] += 2;
}
with output:
paul@horus:~/src/sandbox$ ./genarray
0: 3
1: 4
2: 5
3: 6
4: 7
0: 3
1: 4
2: 5
3: 6
4: 7
0: 3.000000
1: 4.000000
2: 5.000000
3: 6.000000
4: 7.000000
paul@horus:~/src/sandbox$
Of course, in this simple example there's not much in the way of real benefit to writing the add_two()
function compared to just writing add_two_int()
and friends and using them directly. But in a more complex example where you wanted, say, to have something like add_two()
in a library, then this is a way for you to be able to deal with arbitrary and new types without having to modify add_two()
or rebuild and redeploy your library.
Also, I've named the function add_two()
, but it obviously just performs whatever operation you supply to it on each element in turn. So you could write the function subtract_two_int(void * array, size_t idx)
, for instance, pass it to add_two()
, and it'll actually subtract two from each element, despite the name.