When programming in C (or C++) there are three different ways to specify the parameter in a function that takes an array.
Here is an example (implementing std::accumulate
from C++ in C) that shows you what I mean.
I can write it like this:
int accumulate(int n, int *array)
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += array[i];
}
return sum;
}
This can also be written to this (which means the very same thing):
int accumulate(int n, int array[])
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += array[i];
}
return sum;
}
I can also write it like this:
int accumulate(int n, int (*array)[])
{
int i;
int sum = 0;
for (i = 0; i < n; ++i) {
sum += (*array)[i];
}
return sum;
}
All these options are very similar and generate the same executable code but they have a slight difference which is how the caller passes the arguments.
This is how the first two options gets called:
int main(void)
{
int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35};
printf("%d\n", accumulate(ARRAY_LENGTH(a), a));
return 0;
}
This is how the thrid option gets called:
int main(void)
{
int a[] = {3, 4, 2, 4, 6, 1, -40, 23, 35};
printf("%d\n", accumulate(ARRAY_LENGTH(a), &a));
return 0;
}
Note that the third option requires to user to explicitly specify the address of a
with &a
. The first two options does not require this because arrays implicitly gets converted into pointers to the same type in C.
I have always preferred the third approach.
This is why:
It is more consistent with how other types are passed by pointers.
int draw_point(struct point *p); int main() { struct point p = {3, 4}; draw_point(&p); // Here is the 'address of' operator required. }
It makes it possible to use macros like
ARRAY_LENGTH
to get the amount of elements in the array.#include <stdio.h> #define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0])) void this_works(int (*array)[10]) { /* This works! */ printf("%d\n", ARRAY_LENGTH(*array)); } void this_is_invalid_and_dangerous(int array[10]) { /* This does NOT work because `array` is actually a pointer. */ printf("%d\n", ARRAY_LENGTH(array)); }
The only advantage I see with int array[]
(and int *array
) is that you get to write array[X]
instead of (*array)[X]
when you wish to grab an index.
What are your professional thoughts on this? Which approach do you think is better and why? Do you ever mix the two? If so when do you choose what?
Like I said have I always preferred using int (*array)[N]
but I see that the other two approaches are quite common as well.
*int
while the other gets an**int
, so I doubt they'll compile to the same instructions. Furthermore, some of your examples use pointers, others arrays without explicit lengths as in(*array)[]
, and others provide a static length:(*array)[10]
, and I'd think all of those are different. To answer what manner of passing arrays is safe and sensible, you should also state what C standard you are following, and whether you're allowing compiler-specific extensions.size_t function(int (*a)[]){return sizeof(*a);}
. To me it fails to compile usinggcc -std=c99
and prints this error message:main.c: In function ‘function’: main.c:6:18: error: invalid application of ‘sizeof’ to incomplete type ‘int[]’
This suggests that*a
has the typeint[]
and notint *
.gcc -std=c99 -pedantic
follows the C99 standard in each and every point.