为什么这个C代码可以正常运行?

Yan*_* fr 24 c

C代码喜欢这个:

 #include <stdio.h>
 #include <unistd.h> 
 #define DIM(a) (sizeof(a)/sizeof(a[0])) 
 struct obj
 {
     int a[1];
 };
 int main()
 {
     struct obj *p = NULL;
     printf("%d\n",DIM(p->a));
     return 0;
 }
Run Code Online (Sandbox Code Playgroud)

这个对象指针pNULL,所以,我认为这p->a是非法的.但是我已经在Ubuntu14.04中测试了这段代码,它可以正确执行.所以,我想知道为什么......


注意:原始代码在int a[0]上面,但我已经改变了,int a[1]因为每个人似乎都挂在那而不是实际的问题,这是:

sizeof(p->a)当表达式p等于时,表达式是否有效NULL

Chr*_*eck 34

因为sizeof是编译时构造,所以它不依赖于评估输入.仅sizeof(p->a)根据成员的声明类型进行评估p::a,并在可执行文件中成为常量.所以p指向null的事实没有区别.

p播放的运行时值在表达式中绝对没有作用sizeof(p->a).

在C和C++中,sizeof是一个运算符而不是函数.它可以应用于type-id表达式.除了表达式和表达式是一个可变长度数组(在C99中是新的)(如paxdiablo所指出的),表达式是一个评估的操作数,结果与sizeof对照类型相同那个表达的代替.(Cf C11参考由于下面的paxdiablo,C++ 14工作草案5.3.3.1)

  • 另外值得指出`sizeof`是一个运算符,而不是一个函数.可能会减轻一些混乱. (2认同)
  • 长度为零的数组在标准C中是非法的,但在GNU C中允许(强调与标准C不同).灵活的数组成员不计算 - 维度不是0,而是未指定. (2认同)

pax*_*blo 16

首先登场的,如果你想真正便携的代码,你不应该试图创建大小为零的数组1,当你在你原来的问题,现在固定的做到了.但是,因为这不是你的问题是否真正相关的sizeof(p->a)是有效的时候p == NULL,我们可以忽略它.

从C11部分6.5.3.4 The sizeof and _Alignof operators(我的大胆):

2/sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或类型的带括号的名称.所述尺寸从类型确定操作数的.结果是整数.如果操作数的类型是可变长度数组类型,则计算操作数; 否则,不评估操作数,结果是整数常量.

因此,除非它是一个可变长度数组(您的示例不是),否则不会对操作数进行求值.只使用类型本身来确定大小.


1对于那里的语言律师,C11表示6.7.6.2 Array declarators(我的大胆):

1 /除了可选的类型限定符和关键字之外static,[]可以分隔表达式或*.如果它们分隔表达式(指定数组的大小),则表达式应具有整数类型.如果表达式是常量表达式,则其值应大于零.

但是,由于那是在约束部分(其中shall并且shall not不涉及未定义的行为),它只是意味着程序本身并不严格符合.它仍然被标准本身所覆盖.

  • @Jonathan,我不得不在那里不同意,标准允许不符合.它特别提到了符合,严格符合和不符合的程序和实现,并允许实现符合,只要它们的扩展不会改变严格符合程序的行为.因此,虽然代码不是严格符合代码的声明是正确的,但*并不意味着它是符合标准的非法C. (2认同)

M.M*_*M.M 6

此代码包含ISO C中的约束违规,原因如下:

struct obj
{
    int a[0];
};
Run Code Online (Sandbox Code Playgroud)

任何地方都不允许使用零大小的数组.因此,C标准没有定义该程序的行为(虽然似乎有一些争论).

如果编译器实现非标准扩展以允许零大小的数组,则代码只能"正确运行".

必须记录扩展(C11 4/8),所以希望你的编译器文档定义它的行为struct obj(零大小的结构?)和值的值sizeof p->a,以及sizeof当操作数表示零大小的数组时是否评估其操作数.

  • 请注意,GCC是一种编译器,它实现了非标准扩展以允许零大小的数组. (3认同)