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;
}
此对象指针p
为NULL
,因此,我认为此p->a
是非法的。
但是我已经在Ubuntu14.04中测试了这段代码,它可以正确执行。所以,我想知道为什么......
注意:原始代码上面有int a[0]
,但我已将其更改为int a[1]
,因为每个人似乎都挂在那个而不是实际的问题上是:
当sizeof(p->a)
等于p
时,表达式NULL
是否有效?
答案 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 部分(其中shall
和shall 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()
并不关心任何事物的内容,只关注表达式的结果类型。
从C99
和variable length arrays
开始,它是在运行时计算的,当变量长度数组是sizeof
操作数中表达式的一部分时。否则,不评估操作数并且结果是integer constant
Zero-size array
都不允许structs
内的C standard
声明,但是一些较旧的编译器在编译器成为允许incomplete array declarations with empty brackets(flexible array members)
的标准之前允许它。