在大多数情况下,您会发现 sizeof(*x) 实际上根本不计算 *x。而且,由于它是调用未定义行为的指针的求值(取消引用),您会发现它基本上没问题。 C11 标准在6.5.3.4. The sizeof operator /2 中有这样的说法(我在所有这些引用中强调):
sizeof 运算符产生其操作数的大小(以字节为单位),它可以是表达式或带括号的类型名称。大小由操作数的类型决定。结果是一个整数。如果操作数的类型是变长数组类型,则计算操作数;否则,不计算操作数,结果是一个整数常量。
这与 C99 中的同一部分的措辞相同。 C89 的措辞略有不同,因为当时当然没有 VLA。来自3.3.3.4. The sizeof operator:
sizeof 运算符产生其操作数的大小(以字节为单位),它可以是表达式或带括号的类型名称。大小由操作数的类型决定,它本身不会被计算。结果是一个整数常量。
因此,在 C 中,对于所有非 VLA,都不会发生解除引用,并且语句定义明确。如果*x 的类型是 VLA,这被认为是执行阶段sizeof,需要在代码运行时解决 - 所有其他都可以在编译时计算。如果x 本身是VLA,则与其他情况相同,使用*x 作为sizeof() 的参数时不会进行评估。
C++ 的规则(正如预期的那样,因为它是一种不同的语言)略有不同,如标准的各种迭代所示:
首先,C++03 5.3.3. Sizeof /1:
sizeof 运算符在其操作数的对象表示中产生字节数。操作数可以是一个表达式,不计算,或带括号的类型 ID。
在C++11 5.3.3. Sizeof /1,您会发现措辞略有不同,但效果相同:
sizeof 运算符在其操作数的对象表示中产生字节数。操作数要么是一个表达式,它是一个未计算的操作数(第 5 条),要么是一个带括号的类型 ID。
C++11 5. Expressions /7(上面提到的第 5 条)将术语“未评估的操作数”定义为我读过一段时间的最无用、最多余的短语之一,但我不知道脑子里在想什么ISO 人写的时候:
在某些上下文中([对详细说明这些上下文的部分的引用 - pax]),会出现未计算的操作数。 未计算的操作数不会被计算。
C++14/17 与 C++11 具有相同的措辞,但不一定在相同的部分中,因为在相关部分之前添加了内容。它们在 C++14 的 5.3.3. Sizeof /1 和 5. Expressions /8 以及 C++17 的 8.3.3. Sizeof /1 和 8. Expressions /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