是指向无效内存时sizeof(*ptr)未定义的行为
Is sizeof(*ptr) undefined behavior when pointing to invalid memory?
我们都知道,取消引用空指针或指向未分配内存的指针会调用未定义的行为。
但是,当在传递给sizeof
的表达式中使用时,规则是什么?
例如:
int *ptr = 0;
int size = sizeof(*ptr);
这也是未定义的吗?
在大多数情况下,您会发现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
中表达式的类型,并且该类型可以归结为x
的任何类型
您所做的所看到的(除了第一行和最后一行以外的行上不同指针大小的可能性(是:
42
4
4
4
4
42
编号。sizeof
是一个运算符,它处理类型,而不是实际值(未计算(。
为了提醒你这是一个运算符,我建议你养成在可行的情况下省略括号的习惯。
int* ptr = 0;
size_t size = sizeof *ptr;
size = sizeof (int); /* brackets still required when naming a type */
C的答案可能不同,其中sizeof
不一定是编译时构造,但在C++中,提供给sizeof
的表达式永远不会求值。因此,未定义的行为永远不可能表现出来。通过类似的逻辑,您也可以"调用"从未定义的函数[因为函数从未被实际调用,所以不需要定义],这是SFINAE规则中经常使用的事实。
sizeof
和decltype
不计算其操作数,仅计算类型。
sizeof(*ptr)
在这种情况下与sizeof(int)
相同。
由于sizeof不计算其操作数(如果使用C99或更高版本,则可变长度数组除外(,因此在表达式sizeof (*ptr)
中,ptr不计算,因此不会取消引用。sizeof运算符只需要确定表达式*ptr
的类型即可获得适当的大小。
- 编译C++时未定义的引用
- vscode g++链路故障:体系结构x86_64的未定义符号
- 如何修复此错误:未定义对"距离(浮点数,浮点数,浮点数,浮点数,浮点数)"的引用
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 对C宏的未定义引用,但在定义它时会出现重新定义错误
- 未定义的引用在哪里
- 编译时的 CImg 库返回对"__imp_SetDIBitsToDevice"的未定义引用
- 对Py_Initialize()的未定义引用
- c++11评估顺序(未定义的行为)
- 使用mysql c++连接器的未定义引用
- 从python调用openMP共享库时,未定义opnMP函数
- 在 Mac 上使用 CMAKE 将 FFTW 和 FFTWPP 链接到项目中时未定义的符号
- Cmake 链接问题:未定义对 Button::mousePressEvent(QGraphicsSceneMouseE
- 未定义的引用 .. 使用 OpenCV 编译 C++ 代码时,从命令行
- 具有外部"c"和程序集的未定义函数
- 此增量后语句是否会导致未定义的行为?
- 尝试调用 .h 文件中定义的变量时出现变量未定义错误
- ++*ptr++ 在 c++ 中是未定义的行为吗?
- 是指向无效内存时sizeof(*ptr)未定义的行为