首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >未定义、未指定和实现定义的行为

未定义、未指定和实现定义的行为
EN

Stack Overflow用户
提问于 2010-03-07 21:10:30
回答 9查看 78K关注 0票数 632

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

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2010-11-05 10:41:41

未定义的行为是C和C++语言的一个方面,对于来自其他语言的程序员来说可能是令人惊讶的(其他语言试图更好地隐藏它)。基本上,即使许多C++编译器不会报告程序中的任何错误,也可以编写不能以可预测的方式运行的C++程序!

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

代码语言:javascript
运行
复制
#include <iostream>

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节还提到了未定义行为的两个不太危险的兄弟,未指定的行为实现定义的行为

这个国际标准中的语义描述定义了一个参数化的非确定性抽象机器。 抽象机器的某些方面和操作在本国际标准中描述为implementation-defined (例如,sizeof(int))。这些构成了抽象机器的参数。每个实现都应包括描述其在这些方面的特性和行为的文档。 抽象机器的某些其他方面和操作在本国际标准中被描述为未指定的(例如,函数参数的计算顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这些定义了抽象机器的非确定性方面。 在本国际标准中,某些其他操作被描述为未定义的(例如,取消引用空指针的效果)。注意:本国际标准不对包含未定义行为的程序的行为施加任何要求。

具体而言,第1.3.24节规定:

允许的未定义行为包括:完全忽略这种情况,结果不可预测--;在翻译或程序执行过程中,以具有环境特征的方式(无论是否发布诊断消息);终止翻译或执行(通过发布诊断消息)。

你能做些什么来避免遇到未定义的行为?基本上,你必须阅读那些知道自己在谈论什么的作者的好的C++书籍。避免使用网络教程。避免斗牛士。

票数 473
EN

Stack Overflow用户

发布于 2010-03-07 21:15:46

嗯,这基本上是一个标准的直接复制粘贴。

3.4.1 1实现-定义的行为未指定的行为,其中每个实现都记录了如何做出选择 2实现定义行为的一个例子是,当有符号整数右移时,高阶位的传播。 3.4.3 3.4.3 1未定义的行为行为,在使用不可移植或错误的程序结构或错误数据时,本国际标准对此不施加任何要求 2注意,可能的未定义行为包括:完全忽略情况,结果不可预测;在翻译或程序执行过程中,以具有环境特征的记录方式(无论是否发布诊断消息);终止翻译或执行(通过发布诊断消息)。 3例未定义行为的例子是整数溢出的行为。 3.4.4 1未指定行为使用未指定值,或其他行为,其中本国际标准提供了两种或两种以上的可能性,并且不对任何实例中选择的其他行为施加进一步的要求 2例如,未指定行为的一个例子是计算函数的参数的顺序。

票数 115
EN

Stack Overflow用户

发布于 2010-03-07 21:28:19

也许更简单的措辞可能比标准的严格定义更容易理解。

实现-定义的行为:

语言上说我们有数据类型。编译器供应商指定他们应该使用的大小,并提供他们所做工作的文档。

未定义行为:

你做错了什么。例如,int中有一个不适合char的非常大的值。您如何在char中使用该值?其实根本不可能!任何事情都可能发生,但最明智的做法是将该int的第一个字节放入char中。分配第一个字节是不对的,但这就是在幕后发生的事情。

未指定行为:

首先执行这两个函数中的哪一个?

代码语言:javascript
运行
复制
void fun(int n, int m);

int fun1() {
    std::cout << "fun1";
    return 1;
}
int fun2() {
    std::cout << "fun2";
    return 2;
}

//...

fun(fun1(), fun2()); // which one is executed first?

语言没有指定计算,从左到右或从右到左!因此,未指定的行为可能或可能不会导致未定义的行为,但当然,您的程序不应该产生未指定的行为。

@eSKay我认为你的问题值得编辑答案来澄清更多:)

对于fun(fun1(), fun2());,行为不是“定义了实现”吗?毕竟,编译器必须选择一种或另一种方式?

实现--定义的和未指定的--之间的区别在于,编译器应该在第一种情况下选择一个行为,但在第二种情况下不必选择。例如,实现必须有一个并且只有一个sizeof(int)定义。因此,不能说sizeof(int)对于程序的某些部分是4,对于其他部分则是8。与未指定的行为不同,编译器可以说:“好的,我要计算这些参数从左到右,下一个函数的参数是从右到左的。”它可以发生在同一个程序中,这就是为什么它被称为unspecified.事实上,如果指定了一些未指定的行为,C++可能会变得更容易。看看这里的Stroustrup博士对此的回答

有人声称,赋予编译器这种自由和要求“从左到右的普通计算”两者之间的差别是很大的。我不相信,但有无数的编译器“在那里”利用这种自由,一些人热情地捍卫这种自由,改变将是困难的,可能需要几十年的时间才能渗透到C和C++世界的遥远角落。我感到失望的是,并非所有编译器都对代码(如++i+i++ )发出警告。同样,参数的计算顺序也没有具体说明。 国际海事组织留下了太多的“东西”没有定义,没有具体说明,这很容易说,甚至给出例子,但很难解决。还应该指出,避免大多数问题并生成可移植代码并不那么困难。

票数 68
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/2397984

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档