The first is correct and the second is wrong or at least very questionable.
arr when used in an expression or passed to a function, gets adjusted to a pointer to the first element. So it turns equivalent to char*.
In the second example, &arr is one of the few exceptions to this "array decay" rule. Instead of a pointer to the first element, we get a pointer to the whole array, type char(*)[N]. This type is not compatible with char*, so if you would attempt to cast the void* buf to a char* inside the function, the second example causes a subtle bug.
The reason why the second example compiles at all is because void* (and only that type, not void**) is a special pointer type that can be converted to/from any other pointer to object. Should you change the function to
void func(char *buf, size_t size)
Then func(&arr, N); gives the expected compiler diagnostic "passing argument 1 of 'func' from incompatible pointer type".
Now as it happens, if you write the function like this
void func(void *buf, size_t size) {
char* ptr = buf;
for(int i=0; i<size; i++)
{
printf("%c ", ch[i]);
}
}
It will most likely work for either version, because the address of the first item (char*) is the same as the address of the array (char(*)[N]). Also, C's peculiar type system allows us to access a memory location of some type through an array or struct of items of that same type ("the strict aliasing rule").
Best practices:
- Use the first version.
- Avoid the use of
void* when possible, since they are type unsafe.
- Be as explicit with types as you can and don't rely on implicit type conversions.