17

In C99 there are variable-length arrays, and there can be static qualifiers (and type qualifiers) in parameter array declarators:

void f(int i, int *a);
void f(int i, int a[]);
void f(int i, int a[i]);
void f(int i, int a[*]);         // Only allowed in function prototypes.
void f(int i, int a[static i]);

Since array function parameters simply decay to pointers, is there any practical difference between the previous declarations, or is it a matter of style? When should any of them be used? In particular, what does the static qualifier imply? The standard does not render well clear the reason for each syntax.

alecov
  • 4,882
  • 2
  • 29
  • 55

1 Answers1

27

As long as you are working with single-dimensional arrays only, the above declarations are all equivalent. The last one though

void f(int i, int a[static i])

has an extra effect. It is equivalent to the previous ones in terms of the parameter types, but also tells the compiler that it can rely on a parameter pointing to an array of at least i elements (which can be used in optimizations).

You are also forgetting another new declaration

void f(int i, int a[const])

This one actually does have an effect even in case of a single-dimensional array. It is equivalent to

void f(int i, int *const a)

although some might argue that const-qualifications on function parameters are useless. Before it was impossible to const-qualify the pointer the array parameter "decays" to when using the [] syntax for the declaration.

The * (as well as i) between the [] begins to matter only when it is used between the second (or greater) pair of [] in multi-dimensional array declaration. In essence, it is just like it has always been: array size in the parameter declaration always mattered only between the second or further pair of []. The * is used in prototype declarations for VLA parameters, when the size value is not named explicitly. For example, you can declare

void bar(int n, int m, int a[n][m]);

and the compiler will know that a is a VLA since the sizes are not constants. But if you prefer not to name parameters in prototypes, how are you going to tell the compiler that a is a VLA? That's when * helps

void bar(int, int, int a[*][*]);
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 2
    Thanks a lot for your clarification. I suspected at first that the `static` qualifier was related to optimization issues. Also, you forgot to mention that any type qualifiers can be declared in the array declarator, and these may play a much important role (such as `restrict` or `volatile`) than `const` itself. – alecov Sep 12 '10 at 02:18
  • If I do `func(int a[static 4][6])` the static can only be declared within the first brackets. What if I do this `func(int (*a)[static 6])` what will this do? Allow me to prefetch the first 6 values of the first row? And finally what about `func(int *a[static 6])`? – user10607 Oct 31 '14 at 13:44
  • What would be the meaning of `void func1(int a[-1])`, `void func2(int a[some_global++]);`, or `void func3(int length, int dat[length]);`? – supercat Jun 23 '15 at 17:55
  • 2
    @supercat: `void func1(int a[-1])` is illegal. Negative array size is illegal in all contexts. `void func2(int a[some_global++]);` is replaced with `void func2(int a[*]);` and is equivalent to `void func2(int *a)` in a *function prototype* specifically. In a function *definition* this will declare `a` as VLA of corresponding size and *will* increment `some_global` each time the function is called. – AnT stands with Russia Jun 23 '15 at 18:22
  • @AnT: In a function definition, `foo(int length, int x[length])`, the type of `x` is not a VLA, but rather `int*`; does the standard say that a compiler must generate the array type and then ignore it, or ignore it from the get-go? I would expect that pre-C99, invoking the above function with `length` equal to zero would have worked just fine, since the compiler would ignore the dimension, but have no idea whether that would be required for a C99 compiler. – supercat Jun 23 '15 at 18:31
  • @supercat: Well, it is a good question, for which I don't immediately see an answer in the language spec. GCC will increment `some_global` in response to each call to `void func2(int a[some_global++]) {}`, which means that GCC still "remembers" that parameter was declared as VLA even though the parameter type is adjusted to a plain `int *`. – AnT stands with Russia Jun 23 '15 at 20:39
  • @user10607: `static` is only allowed in the first `[]` of parameter declaration of array type, per 6.7.5.2/1. `func(int (*a)[static 6])` is simply illegal since `a` is not of array type. And I don't see anything "unusual" about `func(int *a[static 6])`, i.e. I don't understand what you are asking about in this last case. – AnT stands with Russia Jun 23 '15 at 20:45
  • @AnT: Making VLAs be types seems like a rather curious extension to the language, since it means that things like `typedef` become executable statements. Personally, I wish C had simply defined non-broken `stalloc` and `stfree` methods which worked much like `malloc`, but specified that `stfree` would free the indicated object and everything `stalloc`'ed after it, that a routine which calls `stalloc` must call `stfree` before it returns, and that the state of anything `stalloc`'ed between a `setjmp` and `longjmp` will be indeterminate (meaning that if such things may exist... – supercat Jun 23 '15 at 21:02
  • ...code would need to call `stalloc` before the `setjmp`, and `stfree` that object afterward). Such methods could allow many usage patterns that VLAs don't and could be implemented by *any* platform (using a wrapper around malloc if nothing else), even those incapable of supporting alloca or VLAs. Probably too late now, though... – supercat Jun 23 '15 at 21:04