未定义、未规定和实现定义行为

42 浏览
0 Comments

未定义、未规定和实现定义行为

什么是C和C++中的未定义行为(UB)?未指定行为和实现定义行为呢?它们的区别是什么?

admin 更改状态以发布 2023年5月21日
0
0 Comments

这基本上是从标准中直接复制粘贴的

3.4.1 1 实现定义行为 指未指定行为且每个实现都记录了如何进行选择。

2 例如,实现定义行为的一个示例是:有符号整数右移时,高位位的传播。

3.4.3 1 未定义行为 行为,当使用一个非便携式或错误的程序结构或错误的数据时,该国际标准不对其施加任何要求。

2 注:可能的未定义行为范围从完全忽略情况并具有不可预测的结果,到在翻译或程序执行期间以环境的文件特征方式行事(有或没有发出诊断消息),到终止一个翻译或执行操作(发出诊断消息)。

3 例如,整数溢出下的行为是未定义的。

3.4.4 1 未指定行为 使用未指定值或其他行为,该国际标准在任何情况下提供两个或多个可能性,并未对选择的行为施加进一步的要求。

2 例如,未指定行为的一个示例是函数参数的评估顺序。

0
0 Comments

未定义行为是C和C++语言中的一个特点,对于来自其他语言的程序员可能会感到惊讶(其他语言会更好的隐藏它)。基本上,尽管许多C++编译器不会报告程序中的任何错误,但有可能编写的C++程序表现出不可预测的行为!

让我们看一个经典的例子:

#include 
int main()
{
    char* p = "hello!\n";   // yes I know, deprecated conversion
    p[0] = 'y';
    p[5] = 'w';
    std::cout << p;
}

变量p指向字符串常量"hello!\n",下面两个赋值尝试修改该字符串常量。这个程序做什么?根据C++标准的第2.14.5段11,它调用了未定义行为:

尝试修改字符串常量的效果是未定义的。

我可以听到人们尖叫着说:“等等,我可以编译这个程序,没有问题,并得到yellow作为输出”,或者“你的意思是未定义的,字符串常量存储在只读内存中,因此第一次赋值尝试导致核心转储”。这正是未定义行为的问题所在。基本上,一旦调用未定义行为,标准允许发生任何事情(甚至鼻妖)。如果根据您对语言的思维模型存在“正确”的行为,则该模型显然是错误的;C++标准才是唯一的投票权。

其他未定义行为的例子包括访问超出其界限的数组、解引用空指针、访问其生命周期已结束的对象或编写“聪明的表达式”,如i++ + ++i

C++标准的1.9章节还提到了未定义行为的两个相对较不危险的同类行为,即未规定行为实现定义行为

本国际标准中对语义的描述定义了一台参数化的非确定性抽象机。

本国际标准将该抽象机的某些方面和操作描述为实现定义(例如sizeof(int))。这构成了该抽象机的参数。每个实现都应包括描述它在这些方面的特征和行为的文档。

该抽象机的某些其他方面和操作被描述为未规定(例如函数参数的求值顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这定义了抽象机的非确定性方面。

该抽象机的某些操作被描述为未定义(例如,解引用空指针的效果)。[注意:本国际标准不对包含未定义行为的程序的行为施加任何要求。—end note]

具体而言,1.3.24章节指出:

允许的未定义行为范围从完全忽略这种情况且结果无法预测,到在翻译或程序执行期间以环境特定的记录方式运作(带或不带发出诊断消息),到终止翻译或执行并发出诊断消息。

你可以做些什么来避免遇到未定义的行为? 基本上,你需要阅读那些知道在说什么的作者写的好的 C++ 书籍。 避免互联网教程。 避开 Bullschidt。

0