不完整数组类型的p 和* p的等价性

考虑以下代码(它是由于此讨论而产生的):

#include  void foo(int (*p)[]) { // Argument has incomplete array type printf("%d\n", (*p)[1]); printf("%d\n", p[0][1]); // Line 5 } int main(void) { int a[] = { 5, 6, 7 }; foo(&a); // Line 10 } 

GCC 4.3.4 抱怨错误消息:

 prog.c: In function 'foo': prog.c:5: error: invalid use of array with unspecified bounds 

GCC 4.1.2中的相同错误消息,似乎是-std=c99-Wall-Wextra不变量。

因此它对表达式p[0]不满意,但它对*p很满意,即使它们(理论上)应该是等价的。 如果我注释掉第5行,那么代码会编译并完成我所期望的(显示6 )。

大概有以下之一是真的:

  1. 我对C标准的理解是不正确的,这些表达方式并不相同。
  2. GCC有一个错误。

我把钱放在(1)上。

问题:有人可以详细说明这种行为吗?

澄清:我知道可以通过在函数定义中指定数组大小来“解决”这个问题。 那不是我感兴趣的。


对于“奖励”积分:任何人都可以通过以下消息拒绝第10行时确认MSVC 2010是错误的吗?

 1>\prog.c(10): warning C4048: different array subscripts : 'int (*)[]' and 'int (*)[3]' 

n1570的6.5.2.1节,数组下标:

约束

其中一个表达式应具有类型”指向完整对象类型’的指针,另一个表达式应具有整数类型,结果具有类型”type”。

因此,如果p是指向不完整类型的指针,则标准禁止表达式p[0] 。 间接运算符*没有这样的限制。

但是,在标准的旧版本/草稿中(n1256和C99),该段落中没有“完整”一词。 在标准程序中没有任何参与,我只能猜测这是一个突破性的变化还是一个疏忽的纠正。 编译器的行为表明后者。 p[i]是与*(p + i)相同的标准,而后一个表达式对于指向不完整类型的指针没有意义,因此p[0]可以工作是指向不完整类型的指针,需要一个明确的特殊情况。

我的C有点生疏,但我的阅读是当你有一个int (*p)[]

 (*p)[n] 

说“取消引用p来获得一系列的整数,然后取第n个”。 这看起来很自然。 鉴于此:

 p[n][m] 

说“在p中取第n个数组,然后取该数组的第m个元素”。 这似乎根本没有明确定义; 你必须知道数组有多大才能找到第n个开始的位置。

可能适用于n = 0的特定情况,因为无论数组有多大,第0个数组都很容易找到。 您只是发现GCC没有认识到这种特殊情况。 我不太详细地知道语言规范,所以我不知道这是不是“bug”,但我在语言设计方面的个人品味是p[n][m]应该工作与否,不是当静态地知道n为0时它应该工作,否则不行。

*p <===> p[0]真的是语言规范的确定规则,还是只是一个观察? 在编程时,我不认为取消引用和零索引作为相同的操作。

对于你的“奖励积分”问题(你可能应该将此问题作为一个单独的问题),MSVC10出错了。 请注意,MSVC仅实现C89,因此我使用了该标准。

对于函数调用,C89§3.3.2.2告诉我们:

每个参数都应具有一个类型,使得其值可以分配给具有其相应参数类型的非限定版本的对象。

赋值约束在C89§3.3.16中:

以下之一应该成立:…两个操作数都指向兼容类型的限定或非限定版本的指针,左边指向的类型具有右边指向的所有类型的限定符;

因此,如果两个指针指向兼容类型,我们可以分配两个指针(因此使用指针参数调用带有指针参数的函数)。

各种数组类型的兼容性在C89§3.5.4.2中定义:

对于要兼容的两种数组类型,两者都应具有兼容的元素类型,如果两个大小说明符都存在,则它们应具有相同的值。

对于两个数组类型int []int [3]这个条件显然是成立的。 因此,函数调用是合法的。

 void foo(int (*p)[]) { printf("%d\n", (*p)[1]); printf("%d\n", p[0][1]); // Line 5 } 

这里, p是指向未指定数量的int的数组的指针。 *p访问该数组,因此(*p)[1]是数组中的第二个元素。

p[n]加上指向数组大小的p和n倍,这是未知的。 甚至在考虑[1] ,它就被打破了。 确实零时间任何东西仍为0,但编译器显然会在看到零时立即检查所有术语的有效性而不会发生短路。 所以…

因此它对表达式p [0]不满意,但它对* p很满意,即使它们(理论上)应该是等价的。

正如所解释的那样,它们显然不等同……将p[0]视为p + 0 * sizeof *p ,这显然是为什么……

对于“奖励”积分:任何人都可以通过以下消息拒绝第10行时确认MSVC 2010是错误的吗? 1> \ prog.c(10):警告C4048:不同的数组下标:’int( )[]’和’int( )[3]’

Visual C ++(和其他编译器)可以自由地警告他们认为不是很好的实践,经验上发现经常是错误的事情,或者编译器编写者对非理性的不信任,即使他们是完全合法的标准….可能熟悉的例子包括“比较有符号和无符号”和“有条件的分配(建议用额外的括号括起来)”