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

sas*_*ang 36 c c++ language-lawyer

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

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

例如:

int *ptr = 0;
int size = sizeof(*ptr);
Run Code Online (Sandbox Code Playgroud)

这也未定义吗?

pax*_*blo 48

在大多数情况下,您会发现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操作者产生在其操作数的对象表示的字节数.操作数是表达式(未计算)或带括号的type-id.

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

sizeof操作者产生在其操作数的对象表示的字节数.操作数是一个表达式,它是一个未评估的操作数(第5章),或带括号的type-id.

C++11 5. Expressions /7 (上面提到的第5条)将术语"未评估的操作数"定义为可能是我读过一段时间的最无用,多余的短语之一,但我不知道ISO人员的想法是什么写道:

在某些情况下([某些引用详细说明这些情境的部分 - pax]),会出现未评估的操作数.未评估未评估的操作数.

C++ 14/17与C++ 11 具有相同的措辞,但不一定在相同的部分,因为在相关部分之前添加了东西.他们在5.3.3. Sizeof /15. Expressions /8对C++ 14 8.3.3. Sizeof /18. Expressions /8C++的17.

因此,在C++中,对*xin的评估sizeof(*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';
}
Run Code Online (Sandbox Code Playgroud)

您可能会认为最后一行会输出一些非常不同的东西42(774基于我的粗略计算)因为x已经改变了很多.但是,这是事实并非如此,因为它是唯一的类型的表达在sizeof这里重要,并且类型归结到任何类型x的.

看(不同于指针尺寸上比第一和最后一个其他线路的可能性除外):

42
4
4
4
4
42
Run Code Online (Sandbox Code Playgroud)

  • 为了完整起见,在C++(没有VLA)中永远不会计算表达式:C++ 11,5.3.3说"操作数是表达式,是未评估的操作数(第5章)"或带括号的类型-ID." (3认同)
  • @paxdiablo:显式添加引用空指针是有效的C++代码可能很重要.它在*evaluate*时调用UB,但它*编译*就好了.YSC可能没有意识到这一点. (2认同)
  • @xskxzr同样的原因,`2 + 2`在未评估时不会导致'4`.老实说似乎有些人完全无视"未评估"这几个字 (2认同)

Chr*_*ung 17

编号sizeof是一个操作符,可以处理类型,而不是实际值(未评估).

为了提醒你它是一个操作员,我建议你养成在实际中省略括号的习惯.

int* ptr = 0;
size_t size = sizeof *ptr;
size = sizeof (int);   /* brackets still required when naming a type */
Run Code Online (Sandbox Code Playgroud)

  • 那,并且表达式永远不会被评估 - 它只被解析以确定结果的类型.对于它的价值,我认为`&*ptr`也是合法的. (2认同)

Den*_*ose 6

C的答案可能不同,其中sizeof不一定是编译时构造,但在C++中,sizeof从未评估提供给它的表达式.因此,永远不可能出现未定义的行为.通过类似的逻辑,您还可以"调用"从未定义的函数[因为函数永远不会被调用,不需要定义],这是SFINAE规则中经常使用的事实.


Jon*_*rdy 5

sizeof并且decltype不要仅评估它们的操作数,计算类型.