取消引用空指针在sizeof操作中有效

我遇到了一段代码片段,对我来说应该会因为分段错误而崩溃,但它可以毫无障碍地工作。 有问题的代码加上相关的数据结构如下(上面找到相关的评论):

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])); } 

为什么这样做? 我认为char *的大小将解析为给定体系结构上指针的大小,但是在取消引用NULL -pointer时不应该崩溃和刻录?

为什么这样做?

这是有效的,因为sizeof是一个编译时构造,但根本不评估可变长度数组 。 如果我们看看C99标准草案第6.5.3.4操作员2段的6.5.3.4说( 强调我的 ):

[…]大小由操作数的类型决定。 结果是整数。 如果操作数的类型是可变长度数组类型,则计算操作数; 否则,不评估操作数 ,结果是整数常量。

我们还看到第5段中的以下例子证实了这一点:

 double *dp = alloc(sizeof *dp); ^^^ ^ | This is not the use of uninitialized pointer 

在编译时,要确定表达式的类型以便计算结果。 我们可以通过以下示例进一步certificate这一点:

 int x = 0 ; printf("%zu\n", sizeof( x++ )); 

它不会增加x ,这是非常整洁的。

更新

正如我在回答 为什么sizeof(x ++)不增加x时所说的那样? sizeof是一个编译时操作的例外,也就是它的操作数是一个可变长度数组( VLA )。 虽然我之前没有指出上面6.5.3.4的引用确实这么说。

虽然在C11中与C99相反,但未指定在这种情况下是否评估sizeof

另外,请注意这个问题的C ++版本: 不评估应用sizeof的表达式使得在C ++中取消引用sizeof中的null或无效指针是合法的吗? 。

sizeof运算符是纯编译时操作。 什么都没有运行时,这就是为什么它工作正常。

顺便说一下, path成员实际上不是指针,因此技术上不能为NULL

声明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]中的零点取消引用应该在这里产生未定义的行为。