c++ - 指向无效内存时 sizeof(*ptr) 的行为是否未定义?

标签 c++ c language-lawyer

我们都知道取消引用空指针或指向未分配内存的指针会调用未定义的行为。

但是在传递给 sizeof 的表达式中使用时的规则是什么?

例如:

int *ptr = 0;
int size = sizeof(*ptr);

这也是未定义的吗?

最佳答案

在大多数情况下,您会发现 sizeof(*x) 实际上根本不计算 *x。而且,由于它是调用未定义行为的指针的求值(取消引用),您会发现它基本上没问题。 C11 标准在 6.5.3.4 中有这样的说法。 sizeof 运算符/2(我在所有这些引号中强调):

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand. The result is an integer. If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant.

这与 C99 中相同部分的措辞相同。 C89 的措辞略有不同,因为当时当然没有 VLA。来自 3.3.3.4。 sizeof 运算符:

The sizeof operator yields the size (in bytes) of its operand, which may be an expression or the parenthesized name of a type. The size is determined from the type of the operand, which is not itself evaluated. The result is an integer constant.

因此,在 C 中,对于所有非 VLA,都不会发生解除引用,并且语句定义明确。如果 *x 的类型是 VLA,这被认为是执行阶段 sizeof,需要在代码执行时解决running - 所有其他都可以在编译时计算。如果 x 本身就是 VLA,则与其他情况相同,使用 *x 作为 sizeof() 的参数时不会进行评估>.


C++ 的规则(正如预期的那样,因为它是一种不同的语言)略有不同,如标准的各种迭代所示:

首先,C++03 5.3.3。 sizeof/1:

The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is not evaluated, or a parenthesized type-id.

在,C++11 5.3.3。 sizeof/1,你会发现措辞略有不同但效果相同:

The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause 5), or a parenthesized type-id.

C++11 5. 表达式/7(上面提到的第 5 条)将术语“未计算的操作数”定义为我读过一段时间的最无用、最多余的短语之一,但我不知道 ISO 人在编写它时的想法:

In some contexts ([some references to sections detailing those contexts - pax]), unevaluated operands appear. An unevaluated operand is not evaluated.

C++14/17 与 C++11 具有相同的措辞,但不一定在相同的部分中,因为在相关部分之前添加了内容。它们在 5.3.3 中。 Sizeof/15。 C++14 和 8.3.3 的表达式/8。 Sizeof/18。 C++17 的表达式/8

因此,在 C++ 中,sizeof(*x) 永远 中的 *x 的评估发生,所以它的定义很好,只要你遵循所有其他规则,例如提供完整类型。但是,底线是没有进行取消引用,这意味着它不会导致问题。

您实际上可以在以下程序中看到这种非评估:

#include <iostream>
#include <cmath>

int main() {
    int x = 42;
    std::cout << x << '\n';

    std::cout << sizeof(x = 6) << '\n';
    std::cout << sizeof(x++) << '\n';
    std::cout << sizeof(x = 15 * x * x + 7 * x - 12) << '\n';
    std::cout << sizeof(x += sqrt(4.0)) << '\n';

    std::cout << x << '\n';
}

您可能认为最后一行输出的内容与 42(774,基于我的粗略计算)大不相同,因为 x 已经改变了很多。但实际上并非如此,因为这里只有 sizeof 中表达式的 type 很重要,并且类型归结为任何类型 x 是。

看到的(除了第一行和最后一行之外的指针大小不同的可能性)是:

42
4
4
4
4
42

关于c++ - 指向无效内存时 sizeof(*ptr) 的行为是否未定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7721184/

相关文章:

C++ 转换指针 vector

c++ - GDAL DestroyFeature() 方法产生段错误

c - 每当我尝试使用函数时,我总是收到编译器错误

c++ - 以下内容是否确实违反了 ODR?

c++ - 理解这个合并排序算法?

c++ - 连续信号句柄

c - memset() 没有在 c 中设置内存

c - 结构分隔变量

c - 保证指向标准函数的指针不相等?

c++ - C++ 中允许使用什么 "conversion"的模板模板参数?