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