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

时间:2015-08-19 02:31:56

标签: 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;
 }

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

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

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

4 个答案:

答案 0 :(得分:34)

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

p的运行时值在表达式sizeof(p->a)中绝对没有任何作用。

在C和C ++中,sizeof运算符而不是函数。它可以应用于 type-id 表达式。除了表达式和表达式是可变长度数组(在C99中是新的)(如paxdiablo所指出的),表达式是未评估的操作数,结果与如果你对该表达式的类型采取了sizeof。 (C.f. C11引用下面的paxdiablo,C ++ 14工作草案5.3.3.1)

答案 1 :(得分: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之外,[]可以分隔表达式或*。如果它们分隔表达式(指定数组的大小),则表达式应具有整数类型。如果表达式是常量表达式, 它的值应大于零。

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

答案 2 :(得分:6)

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

struct obj
{
    int a[0];
};

任何地方都不允许使用零大小的数组。因此,C标准没有定义该程序的行为(虽然there seems to be some debate关于那个)。

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

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

答案 3 :(得分:1)

sizeof()并不关心任何事物的内容,只关注表达式的结果类型。

C99variable length arrays开始,它是在运行时计算的,当变量长度数组是sizeof操作数中表达式的一部分时。否则,不评估操作数并且结果是integer constant

任何Zero-size array都不允许structs内的C standard声明,但是一些较旧的编译器在编译器成为允许incomplete array declarations with empty brackets(flexible array members)的标准之前允许它。