在 sizeof 操作中取消引用空指针是不是有效[重复]

Posted

技术标签:

【中文标题】在 sizeof 操作中取消引用空指针是不是有效[重复]【英文标题】:Is dereferencing null pointer valid in sizeof operation [duplicate]在 sizeof 操作中取消引用空指针是否有效[重复] 【发布时间】:2013-11-16 02:55:50 【问题描述】:

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

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

为什么会这样?我认为 sizeofchar* 将解析为给定架构上指针的大小,但它不应该在取消引用 NULL- 时崩溃和烧毁指针?

【问题讨论】:

Please don't cast the return value of malloc() in C. 约阿希姆的权利 (+1)。虽然sizeof 可能在编译器内部,但您通常可以通过查看标准库的offsetof 实现以一种有趣且有形的形式观察到这种语言行为:它可能采用虚构对象的数据成员的地址通过强制转换一个 0/​​NULL 指针...这比sizeof 更接近悬崖,但完全合法。 sizeof(def->path[0]) 定义为 1,因此 return 语句折叠为更具可读性:return malloc(sizeof(RouteDefinition) + nBags); 【参考方案1】:

为什么会这样?

这是因为 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 answer 到Why does sizeof(x++) not increment x? 中所指出的,sizeof 是编译时操作有一个例外,即它的操作数是可变长度数组 (VLA)。虽然我之前没有指出,上面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++?。

【讨论】:

【参考方案2】:

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

【讨论】:

是否有任何情况需要sizeof 的参数中的任何可观察行为来提高语言的表达能力?在sizeof 中计算的值可能会影响其结果的唯一情况是,当它应用于在sizeof 表达式 创建的VLA 类型时,我想不出任何理由允许从而增强语言的表现力。请注意,顺便说一句,即使是 typedef 语句也可以生成可执行代码! @supercat even typedef statements can generate executable code!。你能举个例子吗?谢谢!【参考方案3】:

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

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

【讨论】:

sizeof 是编译时间的例外是 VLA。

以上是关于在 sizeof 操作中取消引用空指针是不是有效[重复]的主要内容,如果未能解决你的问题,请参考以下文章

使用空指针是不是意味着某些内存始终未被使用?

cppcheck 空指针取消引用:m_buffer - 否则检查它是不是为空是多余的

检查指针是不是为空,然后在同一个 if 语句中取消引用它是不是安全?

如何在声纳中解决这个“可能的空指针取消引用”关键问题?

在 C 中取消引用指向 0 的指针

cppcheck 取消引用空指针