考虑以下代码(由于this discussion而产生):
#include <stdio.h>
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 complains,错误消息:
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)上。
问题:有人可以详细说明这种行为吗?
澄清:我知道可以通过在函数定义中指定数组大小来“解决”这个问题。那不是我感兴趣的。
对于“奖励”积分:任何人在拒绝第10行时都可以通过以下消息确认MSVC 2010是错误的吗?
1><snip>\prog.c(10): warning C4048: different array subscripts : 'int (*)[]' and 'int (*)[3]'
答案 0 :(得分:14)
n1570的6.5.2.1节,数组下标:
<强>约束强>
其中一个表达式的类型''指向完整的对象类型'',另一个 expression应具有整数类型,结果类型为''type''。
因此,如果p[0]
是指向不完整类型的指针,则标准禁止使用表达式p
。间接运算符*
没有这样的限制。
在该标准的旧版本/草稿中,(n1256和C99),该段落中没有“完整”一词。在标准程序中没有涉及任何方式,我只能猜测这是一个突破性的变化还是一个疏忽的纠正。编译器的行为表明后者。 p[i]
每个标准与*(p + i)
相同,而后一个表达式对于指向不完整类型的指针没有意义,因此p[0]
能够正常工作p
是指向不完整类型的指针,需要明确的特殊情况。
答案 1 :(得分:4)
我的C有点生疏,但我的阅读是当你有int (*p)[]
时:
(*p)[n]
说“取消引用p
获取一组整数,然后取第n个”。这看起来很自然。鉴于此:
p[n][m]
说“在p中取第n个数组,然后取该数组的第m个元素”。这似乎根本没有明确定义;你必须知道数组有多大才能找到第n个开始的位置。
这个可以适用于n = 0的特定情况,因为无论数组有多大,第0个数组都很容易找到。您只是发现GCC没有认识到这种特殊情况。我不太详细地知道语言规范,所以我不知道这是否是一个“错误”,但我在语言设计方面的个人品味是p[n][m]
应该工作与否,而不是它应该静态地知道n
为0时的工作,而不是其他工作。
*p <===> p[0]
真的是语言规范中的确定规则,还是只是观察?在编程时,我不会将认为的解除引用和零索引作为相同的操作。
答案 2 :(得分:4)
对于您的“奖励积分”问题(您可能应该将此问题作为一个单独的问题),MSVC10出错了。请注意,MSVC仅实现C89,因此我使用了该标准。
对于函数调用,C89§3.3.2.2告诉我们:
每个参数都应具有一个类型,以便可以赋值给它 具有非限定版本类型的对象 相应的参数。
赋值的约束条件是C89§3.3.16:
以下之一应该成立:......两个操作数都指向 兼容类型的合格或非限定版本以及类型 左边指向的所有类型的限定符都指向 在右边;
因此,如果两个指针指向兼容类型,我们可以分配两个指针(因此使用指针参数调用带有指针参数的函数)。
各种数组类型的兼容性在C89§3.5.4.2中定义:
要兼容两种阵列类型,两者都应兼容 元素类型,如果两个大小说明符都存在,它们应该 具有相同的价值。
对于两种数组类型int []
和int [3]
,这种情况显然是成立的。因此,函数调用是合法的。
答案 3 :(得分:0)
void foo(int (*p)[])
{
printf("%d\n", (*p)[1]);
printf("%d\n", p[0][1]); // Line 5
}
此处,p
是指向未指定数量的int
s的数组的指针。 *p
访问该数组,因此(*p)[1]
是数组中的第二个元素。
p[n]
增加了指向数组大小的p和n倍,这是未知的。甚至在考虑[1]
之前,它就被打破了。确实零时间任何一个仍为0,但编译器显然会在看到零时立即检查所有术语的有效性而不会发生短路。所以......
因此它对表达式p [0]不满意,但它对* p感到满意,尽管这些(理论上)应该是等价的。
正如所解释的那样,它们显然不相同......将p[0]
视为p + 0 * sizeof *p
,这显然是为什么......
对于&#34;奖金&#34;要点:任何人都可以通过以下消息拒绝第10行时确认MSVC 2010是错误的吗? 1&gt; \ prog.c(10):警告C4048:不同的数组下标:&#39; int()[]&#39;和&#39; int()[3]&#39;
Visual C ++(以及其他编译器)可以自由地警告他们认为不是好事的事情,经验证明经常是错误的事情,或者编译器编写者只是有一种非理性的不信任的事情,即使他们完全合法化了标准......可能熟悉的例子包括&#34;比较签名和未签名&#34;和&#34;条件内的赋值(建议用额外的括号括起来)&#34;