我遇到了一段代码,对我来说应该会崩溃分段错误,然而它可以顺利运行。有问题的代码加上相关的数据结构如下(上面找到相关的评论):
typedef struct {
double length;
unsigned char nPlaced;
unsigned char path[0];
}
RouteDefinition* Alloc_RouteDefinition()
{
// NB: The +nBags*sizeof.. trick "expands" the path[0] array in RouteDefinition
// to the path[nBags] array
RouteDefinition *def = NULL;
return (RouteDefinition*) malloc(sizeof(RouteDefinition) + nBags * sizeof(def->path[0]));
}
为什么这样做?我认为 sizeof char *将解析为给定体系结构上指针的大小,但在取消引用{{1}时不应该崩溃和刻录}-指针?
答案 0 :(得分:16)
为什么这样做?
这是有效的,因为 sizeof 是一个编译时构造,除了variable length arrays之外根本没有评估。如果我们查看C99 draft standard部分6.5.3.4
sizeof运算符段 2 表示(强调我的):
[...]大小由操作数的类型决定。结果是整数。 如果操作数的类型是可变长度数组类型,则计算操作数;否则,不评估操作数,结果是整数常量。
我们还在段落 5 中看到以下示例,其中确认了这一点:
double *dp = alloc(sizeof *dp);
^^^ ^
|
This is not the use of uninitialized pointer
在编译时,确定表达式的类型以便计算结果。我们可以通过以下示例进一步证明这一点:
int x = 0 ;
printf("%zu\n", sizeof( x++ ));
不会增加x
,这非常简洁。
更新
正如我在my answer到Why does sizeof(x++) not increment x?中所指出的,sizeof
是一个编译时操作的异常,也就是它的操作数是一个可变长度数组( VLA < / em>的)。虽然我之前没有指出上面6.5.3.4
的引用确实这么说。
虽然在C11中与C99相反,但未指明在这种情况下是否评估sizeof
。
另外,请注意此问题的C ++版本:Does not evaluating the expression to which sizeof is applied make it legal to dereference a null or invalid pointer inside sizeof in C++?。
答案 1 :(得分:10)
sizeof
运算符是纯编译时操作。没有什么是运行时,这就是为什么它工作正常。
顺便说一下,path
成员实际上并不是指针,因此技术上不能是NULL
。
答案 2 :(得分:3)
声明sizeof
是一个纯粹的编译时构造(如现有的答案那样)并不完全准确。从C99开始,sizeof
不是纯编译时构造。在操作数类型的运行时评估sizeof
的操作数是VLA。到目前为止发布的答案似乎忽略了这种可能性。
您的代码很好,因为它不涉及任何VLA。但是,这样的事情可能是一个不同的故事
unsigned n = 10;
int (*a)[n] = NULL; // `a` is a pointer to a VLA
unsigned i = 0;
sizeof a[i++]; // applying `sizeof` to a VLA
根据C99标准,应该评估sizeof
的参数(即i
应该增加,参见https://ideone.com/9Fv6xC)。但是,我并不完全确定a[0]
中的空点解除引用应该在这里产生未定义的行为。