emandret
emandret

Reputation: 1399

C array declaration syntax

Declare an array in your include file omitting the first dimension size:

extern float mvp[][4];

Then define the array following the previous declaration in a translation unit:

float mvp[4][4];

No problem. Until you try to get the size of that array in a file which includes the first declaration. Then you would get:

error: invalid application of 'sizeof' to an incomplete type 'float [][4]'

I understand that arrays decays into pointers to their first element when used as lvalue, that array declarations in function prototypes are actually pointers in disguise but here it's not the case. But the first declaration does not declare a pointer, it declares an "incomplete array type" different from:

extern float (*mvp)[4];

When declaring variables, the compiler just reference a "dummy" base address offset and the associated type that the linker will resolve.

I wonder why this "incomplete array type" – which cannot be incremented like a pointer to array but is also not fully an array since its size cannot be retrieved – would be allowed to exist ?

Why not implicitly convert it to a pointer (just a base address offset) or even better, why not throw an error for omitting the size in the first dimension ?


Quoting this

If expression in an array declarator is omitted, it declares an array of unknown size. Except in function parameter lists (where such arrays are transformed to pointers) and when an initializer is available, such type is an incomplete type (note that VLA of unspecified size, declared with * as the size, is a complete type)

So really, the type is incomplete and waiting to be completed later by a later declaration or tentative definition.

Upvotes: 1

Views: 252

Answers (2)

fdk1342
fdk1342

Reputation: 3564

Using extern doesn't make things exist it just used to state that something may exist in a different translation unit. sizeof() can only be used on complete types. This has nothing to do with array pointer decay. extern float (*mvp)[4] is a complete type, it is a pointer to an array of 4 floats. extern float mvp[][4] is incomplete it is a 2D array of floats where one of the dimension is unspecified. These are two very different things. In either case mvp can be used as an array, when using correct syntax, but you can only use sizeof if it can actually determine its size.

Also float mvp[][4] is an array, it's just that its size is indeterminate. What makes it an array is that it's memory is laid out like an array.

Upvotes: 3

It is possible to declare all dimensions of the extern array:

extern float mvp[4][4];

It is just an option to leave the external declaration incomplete and let the definition worry about the dimension. It is useful exactly because the size is not part of its external interface! Should the outermost size change from compilation to another then a translation unit that merely uses the object need not be recompiled.

For this to work, there should probably be a sentinel value that ends the array / a variable that would tell how many elements there are, otherwise it is not very useful.


Why not implicitly convert it to a pointer (just a base address offset) or even better, why not throw an error for omitting the size in the first dimension?

It cannot be converted to a pointer because the declaration is not a definition. It just tells that such an object does exist. The definition of that object exists independent of the external declaration. The actual object that is being declared here is an array, not a pointer.

It is just that in case of arrays the external declaration can declare the outermost dimension or can omit it.


As for the claim that

arrays decays into pointers to their first element when used as lvalue

that is quite wrong. An array expression is an lvalue, and when it decays it is no longer an lvalue - the only case where it stays as an lvalue is as the operand of &.

Upvotes: 2

Related Questions