我们都知道对空指针或未分配内存的指针进行解引用会导致未定义行为。
但是在传递给sizeof
的表达式中使用时,规则是什么?
例如:
int *ptr = 0;
int size = sizeof(*ptr);
这个也是未定义的吗?
我们都知道对空指针或未分配内存的指针进行解引用会导致未定义行为。
但是在传递给sizeof
的表达式中使用时,规则是什么?
例如:
int *ptr = 0;
int size = sizeof(*ptr);
这个也是未定义的吗?
sizeof(*x)
实际上根本不会评估* x
。由于指针的解引用调用未定义的行为,而它是解引用指针的评估,因此您会发现这基本上没问题。C11标准在 6.5.3.4. sizeof运算符/2
(在所有这些引用中都强调了这一点)中有如下规定:
这与C99中同一部分的措辞完全相同。当然,由于那时没有VLAs,因此C89的措辞略有不同。从
sizeof
运算符产生其操作数的大小(以字节为单位),其可以是表达式或类型括号中的名称。大小是从操作数的类型确定的。结果是整数。如果操作数的类型是可变长度数组类型,则对操作数进行评估;否则,不评估操作数,结果是整数常量。
3.3.3.4. sizeof运算符
:
因此,在C中,对于所有非-VLA,不会进行解引用,语句是定义良好的。如果
sizeof
运算符产生其操作数的大小(以字节为单位),其可以是表达式或类型括号中的名称。大小是从操作数的类型本身而不是被求值的类型确定的。结果是整数常量。
*x
的类型是VLA,则被视为执行阶段sizeof
,这需要在代码运行时进行处理-所有其他情况可以在编译时计算得出。如果x
本身是VLA,则与其他情况相同,在使用*x
作为sizeof()
的参数时不会进行评估。
C++03 5.3.3. Sizeof /1
:
在
sizeof
运算符产生其操作数的对象表示中的字节数。操作数可以是表达式,不被评估,或者是括号中的类型标识符。
C++11 5.3.3. Sizeof /1
中,您将找到略有不同的措辞但相同的效果:
sizeof
运算符产生其操作数的对象表示中的字节数。操作数是表达式,是一个未评估的操作数(第5条),或者是括号中的类型标识符。
C++11 5.表达式/7
(上述第5条款)将“未评估的操作数”定义为我最近看过的最无用,最冗余的短语之一,但我不知道ISO人写这篇文章时在想什么:
C ++14/17具有与C ++11相同的措辞,但不一定在相同的部分,因为在相关部在某些情况下( [一些引用到详细说明这些上下文的部分-Pax]),出现了未评估的操作数。 未评估的操作数不会被评估。
#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
表达式的 类型 才是重要的,而该类型最终归结为变量 x
的类型。
除了第一行和最后一行可能有不同的指针大小之外,你看到的是:
42
4
4
4
4
42
*x
未被评估时,为什么不会导致UB? - xskxzr2+2
不会得出 4
。老实说,有些人似乎完全忽略了“未被评估”的这几个词。 - M.Msizeof
运算符中使用的所有表达式都不会被计算,只有它们的类型才是重要的。 - paxdiablo不,sizeof
是一个运算符,作用于类型而不是实际值(该值不会被计算)。
为了提醒您它是一个运算符,建议您在实际中省略括号。
int* ptr = 0;
size_t size = sizeof *ptr;
size = sizeof (int); /* brackets still required when naming a type */
&*ptr
也是合法的。 - Chris Lutzsizeof
并不一定是一个编译时的结构,但在C ++中提供给sizeof
的表达式从未被评估。因此,不存在未定义行为的可能性。由于类似的逻辑,您也可以“调用”从未定义的函数[因为实际上从未调用该函数,所以不需要定义],这在SFINAE规则中经常使用。sizeof
和decltype
不会评估它们的操作数,只计算类型。
sizeof(*ptr)
与sizeof(int)
相同。由于sizeof不评估其操作数(除非您使用C99或更高版本的可变长度数组),因此在表达式sizeof(* ptr)
中,ptr未被评估,因此它未被取消引用。 sizeof运算符只需要确定表达式* ptr
的类型以获取适当的大小。
sizeof
的正确行为,没有任何变化的余地。你提出的另一个问题被关闭并重定向到这个问题,是由于C++某个特定实现中的一个错误所致,并不能减少标准的明确保证。如果你想引用标准中能够使我的答案失效的内容,请尽管这样做,我会根据需要进行更新。 - paxdiablo