25

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?

Community
  • 1
  • 1
dhein
  • 6,431
  • 4
  • 42
  • 74
  • don't waste time to this syntax religious battles. you can always use typedef to define type you want without need to think about such puzzles. my answer: this is syntax error. – lowtech Dec 17 '14 at 15:51
  • 13
    @lowtech What are you talking about? – Sneftel Dec 17 '14 at 15:52
  • I bet it plays with whatever allows VLAs to work dynamically with `sizeof`. – Medinoc Dec 17 '14 at 15:52
  • 1
    @lowtech So it is syntax error because the standard validates this syntax? I'm not quiet sure you got what I'm asking. – dhein Dec 17 '14 at 15:59
  • @Zaibis I just answer the question as C/C++ programmer with over than 20 years of experience. For me this is syntax error. – lowtech Dec 17 '14 at 16:40
  • 1
    @lowtech: As FractalMultiverity points out this is a perfectly reasonable and useful datatype. I agree that generally, when declarations start getting complicated, using typedef to build them up in stages is the better answer -- but a good C programmer should be able to read something at this level of complexity almost at a glance, and a better-than-good C programmer can disentangle much more complicated declarations without too much effort (I've posted my mnemonic for that in past answers), or rattle off a declaration-to-English-description tool in about a day's worth of work. – keshlam Dec 17 '14 at 17:36
  • 1
    (Be conservative and clear in writing; be tolerant in reading because you *will* have to maintain code written by folks who have different conventions and different thresholds of complexity. In any language.) – keshlam Dec 17 '14 at 17:37
  • 7
    @lowtech: "For me this is syntax error" -- The phrase "For me" makes no sense in this context. Either it's a syntax error or it isn't. (The answer may vary with different editions of the C standard.) – Keith Thompson Dec 17 '14 at 19:24
  • @Keith Thompson I am consciously bringing uncertainty into my answer to show that IMHO exact answer is practical nonsense. I do hope that my current compiler will fail to compile this syntax. Otherwise I will be happy to use alternative one once it is available. If someone in ISO wants to have feeling that they are creating something complex and incomprehensible I would suggest to them to switch the field to number theory or combinations: such discipline are complex and incomprehensible. But unlike ISO standards they are real. – lowtech Dec 17 '14 at 22:12
  • 4
    @lowtech excuse my wording, but: Thats bullshit.The standard is the base without you wouldn't even have a compiler that works on more than a single plattform. Without ISO like documents or the rfc you wouldn't even ahve the internet. SO even if its partial nonsense. But your last argument is just.... bullshit. – dhein Dec 17 '14 at 22:14
  • 4
    @lowtech: Well, I guess that's easier than trying to understand what the standard actually says. This: `void func(int(*)[*]);` is syntactically valid in C99 and C11 (not in C90) (at least according to gcc and clang). I haven't yet taken the time to understand what it means, but if *you* think it's a syntax error, then *you* are mistaken. (If you don't happen to like the way C is defined, that's fine.) – Keith Thompson Dec 17 '14 at 22:18
  • @Zaibis maybe you assume that ISO is foundation of C/C++ source code compatibility? The real foundation was always GNU gcc/g++. ISO was sucking new 'features' out of thin air whole nineties and '00. RFCs have nothing to do with it because RFCs is spec of something which may exists and evolve based on real demands. What kind of real demand was to introduce construction like this into C syntax? This is just bureaucracy work to satisfy all committee members. My point: don't waste time on it, do something real. Good news: you works is protected by First Amendment on US territory. – lowtech Dec 17 '14 at 22:28
  • @Keith Thompson without any sarcasm: thanks for letting me to disagree with how C defined by ISO standard and not cursing my position. – lowtech Dec 17 '14 at 22:33
  • A `*` as an array size is only allowed at function-prototype scope, denoting a VLA of unspecified size. As opposed to other arrays of unspecified size, this is a complete type. For example, the translation unit `void foo(int [][]);` is invalid, as an array of incomplete type is specified. `void bar(int [][*]);`, on the other hand, is valid. I don't know of any other difference. – mafso Dec 17 '14 at 23:12
  • http://stackoverflow.com/questions/2312490/ansi-c-grammar-array-declarations-like-et-alii has an example where it appears to be useful. – Mat Jan 06 '15 at 11:17
  • @Mat: Again: Nice to read that article. but it still just explains what it is used for. But my question is about why is there a need for it. (and im not talking about the need to support it because the standard says so. I'm asking about the memory aspect or the operational aspect which would make such kind of feature usefull at all). Because as I see it, there wouldn't be any trouble for a compiler to get void foo(a[][]); in the same way as it could get void foo(a[*][]); – dhein Jan 06 '15 at 12:04

5 Answers5

9

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?

The compiler doesn't need to "know" anything, it's a tool.

The difference between int (*)[*] and int[] is about the same as between int (*)[5] and int[]. If you agree that the latter pair is not interchangeable, then the former isn't either.

In pre-C99, the way to specify an array of unknown number of T elements is T[]. This is an incomplete type, which means you cannot have an array of T[]. There is no T[][]. Inside a function declarator, T[] means the same as T*. OTOH T[*] is a variable-length array, which is different from an array of unknown number of elements. You can have an array of variable-size arrays, i.e. there is T[*][*]. The syntax you are asking about is necessary to support this variable-size-array type. Luckily you are not asking why we need different types, because the answer would be really long-winded, but here's my stab at it.

The purpose of types is two-fold. First, types are needed for object code generation (things like a++ typically generate different object code, depending on the type of a). Second, types are needed for type-checking (things like a++ may be allowed or not depending on the type of a).

The [*] types are only allowed in function declarators that are not parts of function definitions. So code generation and is not relevant here. This leaves us with type checking. Indeed,

int foo(int, int (*)[*]);
int bar(int, int (*)[5]);

int main ()
{
    int a;
    int aa[5];
    int aaa[5][5];

    foo(1, &a);    // incorrect, `&a` is `int*`, `int*` and `int (*)[*]` are different
    bar(1, &a);    // incorrect, `&a` is `int*`, `int*` and `int (*)[5]` are different
    foo(5, aa);    // incorrect, `aa` is `int*` (!), `int*` and `int (*)[*]` are different
    bar(5, aa);    // incorrect, `aa` is `int*` (!), `int*` and `int (*)[5]` are different
    foo(5, &aa);   // correct
    bar(5, &aa);   // correct
    foo(5, aaa);   // correct
    bar(5, aaa);   // correct
}

If we are agree on which calls to bar are correct and which are not, we must agree also on calls to foo.

The only remaining question is, why int foo(int m, int (*)[m]); is not enough for this purpose? It probably is, but the C language does not force the programmer to name formal parameters in function declarators where parameter names are not needed. [*] allows this small freedom in case of VLAs.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • `int[]` is `an array of unknown size`. – Potatoswatter Jan 07 '15 at 08:53
  • @Potatoswatter nope. In a function declarator, `int[]` is exactly the same as `int*`. – n. m. could be an AI Jan 07 '15 at 09:10
  • I'm quoting your statement, "The type an array of unknown size is simply not expressible in pre-C99." This doesn't actually mention parameters. Anyway, since VLA magic happens at the callee, a `int*` prototype is even compatible with a C99 implementation using a VLA parameter. – Potatoswatter Jan 07 '15 at 10:57
  • @Potatoswatter `int[*]` is compatible with `int*` because of array-to-pointer decay, but they are different types nevertheless. `int(*)[*]` is very different from, and incompatible with, `int**`. You can use `int*` instead of`int[*]` or `int[]` *at the top level of function parameter declarators*, but not necessarily in other contexts. – n. m. could be an AI Jan 07 '15 at 11:38
  • In any case, the type of an array of unknown size in C89 is `T[]`. A more accurate statement would be, "The type of a multidimensional array of unknown inner dimension cannot be expressed in pre-C99." – Potatoswatter Jan 07 '15 at 13:16
  • OK so I was mistaken, will fix the answer. – n. m. could be an AI Jan 07 '15 at 13:45
6

I am going to answer your question strictly as asked:

This Question primaly aims on "Is it even neccessary to support this for a compiler?"

For a C99 compiler, yes: it is part of the standard so a C99-conforming compiler must support it. The question of what int foo[*]; is useful for is quite orthogonal to the question of whether it must be supported. All compilers claiming to conform to C99 that I tested supported it (but I am not sure what it is useful for, either).


For a C11 compiler, good news! Variable-Length Arrays have been made a “conditional feature”. You can implement a C11-compliant without Variable-Length Arrays as long as it defines __STDC_NO_VLA__:

6.10.8.3 Conditional feature macros

__STDC_NO_VLA__ The integer constant 1, intended to indicate that the implementation does not support variable length arrays or variably modified types.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • 1
    And again I got missunderstood. Im not asking about the compiler developer side of view. I clearly stated: I'm asking, what made the commitee claim this need. What would this feature had been in any form needed for. Or at least usefull. Is there ayn reason that made the comittee see this as neccessary for compilers. And you answer "Yes, because they said so".... – dhein Jan 06 '15 at 11:39
  • @Zaibis I might be wrong, but I think you asked "Is it even neccessary to support this for a compiler?". Let me check… Yes, you did. – Pascal Cuoq Jan 06 '15 at 12:17
  • Please read the full note. I said thats the direction my question is aiming. and the last sentence clearly says "So is this realy neccessary to be supported? And if not so, why the standard even is implementing this semantic?" And for sure they didn't implement it, because they were forced by them self. So I thought, THIS is self explanatory. – dhein Jan 06 '15 at 12:24
1

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.

  • 1
    More depth in [“\[*\]” parameter of a function](http://stackoverflow.com/a/17371914/153285). In particular, it only works for prototypes. The compiler never takes a `*` and maps it to a particular parameter. – Potatoswatter Jan 07 '15 at 08:49
0
In C99 it is possible to declare arrays using variable dimensions, providing the variable has a ( positive integer ) value at the time the declaration is made. It turns out that this carries over to the declaration of arrays in function parameters as well, which can be particularly useful for multi-dimensional arrays.
For example, in the prototype:

int arrayFunction( int nRows, int nCols, double x[ nRows ], 
                   double y[ nRows ][ nCols ] );

the variable dimension on x is informative to the human but not necessary for the computer, since we could have declared it as x[ ]. However the nCols dimension on y is very useful, because otherwise the function would have to be written for arrays with pre-determined row sizes, and now we can write a function that will work for arrays with any row length.

For two array types to be compatible, both must have compatible element types, and if both size specifiers are present and are integer constant expressions, then both sizes must have the same value. A VLA is always compatible with another array type if they both have the same element type. If the two array types are used in a context that requires them to be compatible, it is undefined behavior if the dimension sizes are unequal at run time

S.Singh
  • 1
  • 1
-1

It might be useful if you want to work with "jagged" arrays, when the size of the rows of that matrix, while unknown at compile-time, will be initialized in run-time and the remain the same during the whole execution time. But to make sure you will stay in bounds for each row, you will have to store actual sizes of that array somehow, separately for each row if want it "jagged", because sizeof operator will not work properly for run-time initialized arrays (it will return the size of the pointer at best, since it's a compile-time operator).

  • Or if the pointer will otherwise be, at different times, pointing to different arrays. (With the same caveat that it's your responsibility to either store the length somewhere or -- as in strings -- to have a "terminator" value in the last position that your code knows not to read/write past.) ... If you don't need it, don't worry about it. When you do need it, C makes it possible. – keshlam Dec 17 '14 at 17:33
  • "because sizeof operator will not work properly for run-time initialized arrays" – if you mean a VLA (variable-length array) by "run-time initialized array", then this is clearly wrong. And so is "since it's a compile-time operator". The `sizeof` operator is a run-time construct when applied to a VLA. – The Paramagnetic Croissant Jan 06 '15 at 11:03
  • 1
    Re previous: [see this example](http://coliru.stacked-crooked.com/a/487e991b68c5d594). – The Paramagnetic Croissant Jan 06 '15 at 11:10
  • 1
    C doesn't support jagged arrays, no idea where this is coming from. – Potatoswatter Jan 07 '15 at 08:54