71

While trying to implement a C11 parser (for educational purposes), I found that in C11 (p. 470) but also in C99 (p. 412) (thanks Johannes!), the direct declarator is defined as:

(6.7.6) direct-declarator:  
    direct-declarator [ type-qualifier-list? * ]

At first, I thought this was an error in the grammar (the type list shouldn't be optional). However, when I tried this out in my reference compiler (clang), I got an rather unexpected error:

int array[*] = { 1, 2, 3 };
// error: star modifier used outside of function prototype

So apparently, (in clang) this is called the star modifier.

I quickly learned that they can only be used in function signatures:

void foobar(int array[*])

However, they can only be used in the declaration. Trying to use it in a function definition results in an error as well:

void foobar(int array[*]) {
    // variable length array must be bound in function definition
}

So as far as I can tell, the intended behaviour is to use [*] in the function declaration and then use a fixed number in the function definition.

// public header
void foobar(int array[*]);

// private implementation
void foobar(int array[5]) {

}

However, I have never seen it and I don't quite understand the purpose of it either.

  1. What is its purpose, why was it added?
  2. What's the difference with int[]?
  3. What's the difference with int *?
haccks
  • 104,019
  • 25
  • 176
  • 264
cutsoy
  • 10,127
  • 4
  • 40
  • 57
  • 3
    It's been in C since C99. I believe it means "placeholder for VLA number here". `int[][]` is not valid, but `int[][*]` is (in a non-defining function prototype). – Johannes Schaub - litb Aug 04 '16 at 19:03
  • 1
    You're right, I forgot to mention it but I searched for the errors that I got from clang and they corresponded to their VLA unit test suite. Still, I would like to know their purpose. Thanks! – cutsoy Aug 04 '16 at 19:04
  • 1
    22 views and this is already the first google result for "c star modifier". You've hit upon an obscure feature. – ApproachingDarknessFish Aug 04 '16 at 19:10
  • Since when is Clang "the reference compiler"? – user2357112 Aug 04 '16 at 19:11
  • @ApproachingDarknessFish there's other questions about it, this is the first time someone called it "star modifier" though – M.M Aug 05 '16 at 04:27
  • 1
    @user2357112 I don't think he meant it in any specific capacity, just that it was the compiler that he personally is using to check that his interpretation of the standard is sane. – Random832 Aug 05 '16 at 05:23

2 Answers2

42

What is its purpose, why was it added?

Purpose is seen when a variable length two dimentional array is used as a function parameter. The function

int foo(int n, int m, int a[n][m])  {...}   

can be prototyped as any of the following

int foo(int , int, int [][*]);
int foo(int , int, int a[*][*]);
int foo(int , int, int (*a)[*]);
int foo(int n, int, int a[n][*]);
int foo(int , int m, int a[*][m]);
int foo(int , int m, int (*a)[m]);
int foo(int n, int m, int a[n][m]); 

In case of two dimensional array, when used as function parameter, size of the second dimension can't be omitted. If the name of first variables in function prototype is omitted then it wouldn't be possible to specify the length (second dimension) of the array. The * gives the clue that the length of the array will be determined by the second parameter.

What's the difference with int[]?
What's the difference with int *?

In case of 1D array, for the function definition

int bar(int n, int a[n]} {...}  

any of the following prototype is valid

int bar (int , int *);
int bar (int , int [*]);
int bar (int , int []);
int bar (int n, int a[]);
int bar (int n, int a[n]);
int bar (int n, int [n]);   

In this case neither * nor n is necessary as compiler will treat both of int [*] and int [n] as int *. So, with one dimensional array you can't see much difference.


NOTE: When using variable length array as a function parameter, order of parameter is important. Order of parameters for first four prototypes of bar can be switched, but in latter two first parameter must not be the array itself.

int bar (int a[n], int n);  //Wrong. Compiler has not yet seen 'n'.
  
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 6
    This directly answers the question instead of just quoting a standard. – alvits Aug 04 '16 at 19:15
  • 5
    The standard quote directly answers the question, and it's authoritative, because it's from the standard. There's nothing wrong with standard quotes. (Come to think of it, that quote's from the rationale document, not the standard, though.) – user2357112 Aug 04 '16 at 19:18
  • @user2357112 there was another answer not by me who directly quoted the Standard. I guess alvits refes to that. Mine doesn't quote the C standard, as you correctly point out – Johannes Schaub - litb Aug 04 '16 at 19:22
  • `int bar (int [*], int);` is correct. – M.M Aug 05 '16 at 04:30
  • 9
    It's not that there's anything *wrong* with quoting the standard, *per se*. In fact, it's usually a good idea. The problem comes when the answer consists solely of a quotation from the standard without any sort of explanation. Generally, if someone was able to read and understand exactly what was meant by the standard, they wouldn't be asking a question about it on Stack Overflow. So a good answer (to a question where it is relevant) really needs to do both. If you had to choose one or the other, I'd have to agree with alvits and say that an explanation is superior to a quotation. – Cody Gray - on strike Aug 05 '16 at 04:38
16

The C rationale document for C99 says

A function prototype can have parameters that have variable length array types (§6.7.5.2) using a special syntax as in

int minimum(int, int [*][*]);

This is consistent with other C prototypes where the name of the parameter need not be specified.


What's the difference with int[]

What's the difference with int *.

I think it's simply that those types in a function prototype means "pointer", while a [*] in a non-top position (int[*] still equals int[] I think, in a function prototype) actually is valid and means array

// not recommended though: it is now unclear what the parameters
// mean to human callers!
void f(int, int [][*]);

void f(int n, int x[][n]) {
    x[1][0] = 1;
}

int main() {
   int a[2][1];
   f(1, a);
   printf("%d\n", a[1][0]);
}

As for the purpose, when indexing the array in the function definition, the compiler needs to know how many integers of the next index to skip when giving the first index (x[i] skips i * n integers in f above). But this information is not needed in the non-defining prototype declaration, hence it can be left out and replaced by *.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 2
    Your function prototype should be `void f(int, int x[][*])`. When you give the first parameter a name, then there's no need for the `*`. The whole point of this feature is that you can use old fashioned function prototypes that don't name the parameters. – user3386109 Aug 04 '16 at 19:33
  • @user3386109 hmm, was naming the parameters not allowed in C89? I thought this was always allowed? Why woud leaving out names be "old fashioned"? But, true, the motivation is less high if you name the parameter anyway – Johannes Schaub - litb Aug 05 '16 at 06:55