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

时间:2013-11-05 09:25:41

标签: c sizeof dereference null-pointer

我遇到了一段代码,对我来说应该会崩溃分段错误,然而它可以顺利运行。有问题的代码加上相关的数据结构如下(上面找到相关的评论):

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}时不应该崩溃和刻录}-指针?

3 个答案:

答案 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 answerWhy 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]中的空点解除引用应该在这里产生未定义的行为。