24

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

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 *的大小将解析为给定架构上指针的大小,但它不应该在取消引用 -pointer 时崩溃和烧毁NULL

4

3 回答 3

19

为什么这行得通?

这是有效的,因为sizeof是一个编译时构造,除了可变长度数组根本不被评估。如果我们查看C99 草案标准部分6.5.3.4 The sizeof operator2段说(强调我的):

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

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

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

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

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

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

更新

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

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

另外,请注意此问题有一个 C++ 版本:Does not evaluate the expression to which sizeof is applied使得在C++中取消引用sizeof中的空指针或无效指针是合法的?.

于 2013-11-05T10:28:36.210 回答
10

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

顺便说一句,path成员实际上不是指针,所以从技术上讲它不可能是NULL.

于 2013-11-05T09:26:48.443 回答
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)。但是,我不完全确定 null 点取消引用a[0]应该在这里产生未定义的行为。

于 2015-06-10T21:52:55.440 回答