views:

344

answers:

3

The ANSI C grammar from -link- give me the following rules for array declarations:

 (1) | direct_declarator '[' type_qualifier_list assignment_expression ']'
 (2) | direct_declarator '[' type_qualifier_list ']'
 (3) | direct_declarator '[' assignment_expression ']'
 (4) | direct_declarator '[' STATIC type_qualifier_list assignment_expression ']'
 (5) | direct_declarator '[' type_qualifier_list STATIC assignment_expression ']'
 (6) | direct_declarator '[' type_qualifier_list '*' ']'
 (7) | direct_declarator '[' '*' ']'
 (8) | direct_declarator '[' ']'

Now I have a some questions about these:

  • Can I use (1) - (6) except (3) only in C99?
  • What are (4) and (5) for? The keyword 'static' confuses me.
  • Where to use (6)?
  • What's the difference between the following two function prototypes:

    void foo(int [*]); and

    void foo(int []);

Thank you.

A: 

My K&R2nd (which covers and includes the ANSI standard) does not seem to say anything about [*] either in the text, or in the standard itself. Nor can I make the official grammar in the standard accept that syntax.

It may be related to K&R c (though I don't seem to recall it), may have been a common extension, or have been a proposal that ultimately didn't make the standard.

I would assume it makes the dimension of the array explicitly unspecified. But I'm just guessing.


Hmm...gcc accepts

#include <stdio.h>

void f(int s, int a[*]);

int main(void){
  int a[2] = {0};
  f(2,a);
  return 0;
}

void f(int s, int a[]){
  int i;
  for (i=0; i<s; ++i){
    printf("%d\n",a[i]);
  }
}

in ansi, c89 and c99 mode; issuing no warnings even with -Wall. Note that it did not like the [*] syntax in the function definition. Adding -pedantic made it complain about the [*] syntax in c89 and ansi modes, but it continued to accept in in c99 mode.

dmckee
Clifford
Regarding not liking the [*] in the function definition, the standard says [*] "is a variable length array type of unspecified size,which can only be used in declarations with *function prototype* scope"
Clifford
A: 

I hope you are not trying to learn C grammar from a yacc specification!? The link you posted appears to be based on the ISO C99 draft. The relevant section is 6.7.5.2. The wording is arcane (but less so than the yacc syntax perhaps!)

Clifford
No, I'm not learning C with that ;-)
tur1ng
@tur1ng: It is just that you said "The ANSI C grammar from -link- give *me* the following rules for array declarations". I suggest that those rules were intended for yacc consumption, not human consumption, and that the ISO standard is intended for humans (and is the source document for the yacc). And I did say "C grammar" rather than just "C"; you can learn C to a working level without intricate knowledge of the grammar. Perhaps you are writing a compiler or static analyser then?
Clifford
Oh sorry, I was reading too fast. Yes it is for a static analyser.
tur1ng
+6  A: 

You can't use type qualifiers or static in size portion of array declaration in C89/90. These features are specific to C99.

static in array declaration tells the compiler that you promise that the specified number of elements will always be present in the array passed as the actual argument. This might help compilers to generate more efficient code. If you violate your promise in the actual code (i.e. pass a smaller array), the behavior is undefined. For example,

void foo(int a[static 3]) {
  ...
}

int main() {
  int a[4], b[2];
  foo(a); /* OK */
  foo(b); /* Undefined behavior */
}

The * in size portion of array declaration is used in function prototype declarations only. It indicates that the array has variable length (VLA). For example, in the function definition you can use a VLA with a concrete run-time size

void foo(int n, int a[n]) /* `a` is VLA because `n` is not a constant */
{
  ...
}

When you declare the prototype you can do the same

void foo(int n, int a[n]); /* `a` is VLA because `n` is not a constant */

but if you don't specify the parameter names (which is OK in the prototype), you can't use n as array size of course. Yet, if you still have to tell the compiler that the array is going to be a VLA, you can use the * for that purpose

void foo(int, int a[*]); /* `a` is VLA because size is `*` */

Note, that the example with a 1D array is not a good one. Even if you omit the * and declare the above function as

void foo(int, int a[]);

then the code will still work fine, because array type decays to pointer type anyway. But once you start using multi-dimensional arrays, the proper use of * becomes important. For example, if the function is defined as

void bar(int n, int m[n][n]) { /* 2D VLA */
  ...
}

the the prototype might look as follows

void bar(int n, int m[n][n]); /* 2D VLA */

or as

void bar(int, int m[*][*]); /* 2d VLA */

In the latter case the first * can be omitted (because of the array-to-pointer decay), but not the second *.

AndreyT
I suppose the place where this really matters is when you do `f(int, int, int a[*][*])` or the like...
dmckee
@dmckee: I was just adding that to my answer :)
AndreyT
Lots of people thinking along the same lines around here. Great minds and all that...
dmckee