Search code examples
carrayspointerspass-by-reference

Is using array arguments in C considered bad practice?


When declaring a function that accesses several consecutive values in memory, I usually use array arguments like

f(int a[4]);

It works fine for my purposes. However, I recently read the opinion of Linus Torvalds.

So I wonder if the array arguments are today considered obsolete? More particularly,

  • is there any case where the compiler can utilize this information (array size) to check out-of-bound access, or
  • is there any case where this technique brings some optimization opportunities?

In any case, what about pointers to arrays?

void f(int (*a)[4]);

Note that this form is not prone to "sizeof" mistakes. But what about efficiency in this case? I know that GCC generates the same code (link). Is that always so? And what about further optimization opportunities in this case?


Solution

  • If you write

    void f(int a[4]);
    

    that has exactly the same meaning to the compiler as if you wrote

    void f(int *a);
    

    This is why Linus has the opinion that he does. The [4] looks like it defines the expected size of the array, but it doesn't. Mismatches between what the code looks like it means and what it actually means are very bad when you're trying to maintain a large and complicated program.

    (In general I advise people not to assume that Linus' opinions are correct. In this case I agree with him, but I wouldn't have put it so angrily.)

    Since C99, there is a variation that does mean what it looks like it means:

    void f(int a[static 4]);
    

    That is, all callers of f are required to supply a pointer to an array of at least four ints; if they don't, the program has undefined behavior. This can help the optimizer, at least in principle (e.g. maybe it means the loop over a[i] inside f can be vectorized).

    Your alternative construct

    void f(int (*a)[4]);
    

    gives the parameter a a different type ('pointer to array of 4 int' rather than 'pointer to int'). The array-notation equivalent of this type is

    void f(int a[][4]);
    

    Written that way, it should be immediately clear that that declaration is appropriate when the argument to f is a two-dimensional array whose inner size is 4, but not otherwise.

    sizeof issues are another can of worms; my recommendation is to avoid needing to use sizeof on function arguments at almost any cost. Do not contort the parameter list of a function to make sizeof come out "right" inside the function; that makes it harder to call the function correctly, and you probably call the function a lot more times than you implement it.