C语言 取消引用空指针在 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 指针时它不应该崩溃和刻录吗?

为什么会这样?

这是有效的,因为 sizeof 是一个编译时构造,除了可变长度数组之外,根本不计算。如果我们看一下C99草案标准部分6.5.3.4运算符的大小2段说(强调我的):

[...]大小由操作数的类型确定。结果是一个整数。如果操作数的类型是可变长度数组类型,则计算操作数;否则,不计算操作数,结果为整数常量。

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

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

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

int x = 0 ;
printf("%zun", sizeof( x++ ));

这不会增加x,这很整洁。

更新

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

尽管在 C11 中而不是 C99 中,在这种情况下是否评估sizeof是不确定的。

另外,请注意,此查询有一个C++版本:不计算应用 sizeof 的表达式是否使得取消引用 sizeof 中的空指针或无效指针是合法的 C++?。

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

最新更新