C Programming – int* vs int[N] vs int(*)[N] in Function Parameters

apiapi-designccoding-stylefunctions

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.

Best Answer

In practice, you'll see

int accumulate( int n, int *array)

most often. It's the most flexible (it can handle arrays of different sizes) and most closely reflects what's happening under the hood.

You won't see

int accumulate( int (*array)[N] )

as often, since it assumes a specific array size (the size must be specified).

If your compiler supports variable-length array syntax, you could do

int accumulate( size_t n, int (*array)[n] )

but I don't know how common that is.

Related Topic