在包含文件中声明一个数组,省略第一个尺寸:
extern float mvp[][4];
然后在翻译单元中的前一个声明之后定义数组:
float mvp[4][4];
没问题。直到尝试获取包含第一个声明的文件中该数组的大小为止。然后您将得到:
error: invalid application of 'sizeof' to an incomplete type 'float [][4]'
我知道数组在用作左值时会分解为指向其第一个元素的指针,函数原型中的数组声明实际上是伪装的指针,但事实并非如此。但是第一个声明没有声明指针,而是声明了一个不同于以下内容的“不完整数组类型”:
extern float (*mvp)[4];
声明变量时,编译器仅引用“虚拟”基地址偏移量以及链接程序将解析的关联类型。
我想知道为什么这种“不完整的数组类型”(它不能像指向数组的指针那样递增,但由于无法获取其大小而不能完全作为数组)将被允许存在吗?
为什么不将其隐式转换为指针(仅是基地址偏移量)甚至更好,为什么不为忽略一维尺寸而抛出错误?
答案 0 :(得分:3)
使用extern
不会使事物存在,它只是用来说明某些事物可能存在于不同的翻译单元中。 sizeof()
仅可用于完整类型。这与数组指针衰减无关。 extern float (*mvp)[4]
是一个完整类型,它是一个指向4个浮点数的数组的指针。 extern float mvp[][4]
不完整,它是2D浮点数组,其中未指定维度之一。这是两件事。无论哪种情况,在使用正确的语法时,mvp
都可以用作数组,但是只有在sizeof
可以实际确定其大小的情况下,您才能使用它。
float mvp[][4]
也是一个数组,只是其大小不确定。使它成为数组的原因是它的内存像数组一样布置。
答案 1 :(得分:2)
可以声明extern
数组的所有尺寸:
extern float mvp[4][4];
这只是一个选择,可以使外部声明不完整,让定义担心尺寸。 精确很有用,因为大小不是其外部接口的一部分!如果最外层的大小从编译更改为另一个,则不必重新编译仅使用对象的转换单元。
要执行此操作,可能应该有一个定点值(sentinel value)结束数组/一个变量,该变量将告诉您有多少个元素,否则不是很有用。
为什么不将其隐式转换为指针(只是基地址偏移量)甚至更好,为什么不为忽略第一维尺寸而抛出错误?
由于声明不是定义,因此无法将其转换为指针。它只是告诉我们确实存在这样的对象。该对象的定义独立于外部声明而存在。在此声明的实际对象是一个数组,而不是一个指针。
只是在数组的情况下,外部声明可以声明最外面的维度或可以忽略它。
关于该主张
数组用作左值时会衰减为指向其第一个元素的指针
那是完全错误的。数组表达式是是左值,当它衰减时不再是左值-唯一保持左值的情况就是&
的操作数。