Search code examples
cc99

Why is the syntax "int (*)[*]" necessary in C?


Just was looking something up in the ISO/IEC9899 When I stumbled on this:

6.7.6 Type names

[...]

Semantics

2 In several contexts, it is necessary to specify a type. This is accomplished using a type name, which is syntactically a declaration for a function or an object of that type that omits the identifier.128) 3 EXAMPLE The constructions

(a) int
(b) int *
(c) int *[3]
(d) int (*)[3]
(e) int (*)[*]
(f) int *()
(g) int (*)(void)
(h) int (*const [])(unsigned int, ...)

name respectively the types (a) int, (b) pointer to int, (c) array of three pointers to int, (d) pointer to an array of three ints, (e) pointer to a variable length array of an unspecified number of ints, (f) function with no parameter specification returning a pointer to int, (g) pointer to function with no parameters returning an int, and (h) array of an unspecified number of constant pointers to functions, each with one parameter that has type unsigned int and an unspecified number of other parameters, returning an int.

What most confused me was:

(e) pointer to a variable length array of an unspecified number of ints

The others I can understand more or less. But what is the use of a pointer to a VLA of unspecified number of 'ints'?

And is there even a need for compiler's to support the syntax of

int foo[*];

?

EDIT for clarification

This Question primaly aims on "Is it even neccessary to support this for a compiler?". Whilest this post ANSI-C grammar - array declarations like [*] et alii clearly improved my knowledge. There is still no answer for: Why does the compiler need to know if the parameter of the prototype just is a address containing unknown size. as with simply doing int foo[] or it will be unspecified size?

So is this realy neccessary to be supported? And if not so, why the standard even is implementing this semantic?


Solution

  • If I pass an array with more than one dimension to a function, and if the function parameters used to express the number of elements in a given dimension of the array come after the array parameter itself, the [*] syntax may be used. In the case of an array with more than two dimensions, and if the array parameter, again, precedes the element count parameters, this syntax must be used, as array decay only ever occurs once. After all, you can't very well use int (*)[][] or int [][][] because the standard requires that in int [A][B] and int [A][B][C][D], only A may be omitted due to the array decaying to a pointer. If you use pointer notation in the function parameter, you're allowed to use int (*)[], but this makes very little sense to me, especially since:

    • sizeof ptr[0] and sizeof *ptr are both illegal -- how should the compiler determine the size of an array that has an indeterminate element count? Instead you must find it at runtime using N * sizeof **ptr or sizeof(int (*)[N]). This also means that any arithmetic operations on ptr, such as the usage of ++ptr, are illegal since they rely upon the size information, which cannot be calculated. Type casts may be used to get around this, but it is easier just to use a local variable with the proper type information. Then again, why not just use the [*] syntax and include the proper type information from the start?
    • sizeof ptr[0][0] is illegal, but sizeof (*ptr)[0] is not -- array indexing is still performed even when simply getting info like size, so it is like writing sizeof (*(ptr + 0))[0], which is illegal because you cannot apply arithmetic operations to an incomplete type as previously mentioned.
    • Someone who has never encountered this issue before might think [] can be replaced by *, yielding int ** instead of int (*)[], which is incorrect because that sub-array has not decayed. Array decay only occurs once.

    I noted that the [*] syntax is unnecessary if the parameters used as element counts came first, which is true, but when is the last time anybody saw any of the following?

    void foo (int a, int b, int c, int arr[a][b][c]);
    
    void bar (int a, int b, int c, int arr[][b][c]);
    
    void baz (int a, int b, int c, int (*arr)[b][c]);
    

    So to answer your question:

    • if a function is able to operate upon multidimensional arrays of various lengths (or a pointer to a 1-D array),

    and

    • the parameters denoting element count are listed after the array parameter itself,

    the [*] syntax may be required. I actually encourage usage of [*] since [] comes with problems when size information is required.