未定义,未指定和实现

C和C ++中未定义,未指定和实现定义的行为之间有什么区别?


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

我们来看一个典型的例子:

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

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

抽象机器的某些方面和操作在本标准中被描述为实现定义的 (例如, sizeof(int) )。 这些构成抽象机器的参数。 每个实施应包括描述其在这些方面的特点和行为的文件。

本标准中抽象机器的某些其他方面和操作未作规定 (例如,函数参数的评估顺序)。 在可能的情况下,本标准定义了一系列允许的行为。 这些定义了抽象机器的非确定性方面。

本标准中某些其他操作未定义 (例如,取消引用空指针的效果)。 [注意: 本国际标准对含有未定义行为的程序行为没有要求。 - 注意]

具体而言,第1.3.24节规定:

允许的未定义的行为范围从完全忽略情况,具有不可预测的结果 ,在翻译或程序执行期间以文档化的方式表现环境特征(发布或不发布诊断消息),终止翻译或执行(发布的诊断消息)。

你能做些什么来避免遇到未定义的行为? 基本上,你必须由知道他们在谈论什么的作者阅读好的C ++书籍。 拧互联网教程。 拧bullschildt。


那么,这基本上是从标准直接复制粘贴

3.4.1 1个实现定义的行为未指定的行为,其中每个实现记录了如何进行选择

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

3.4.3 1 未定义的行为行为,在使用不可移植或错误的程序结构或错误的数据时,本国际标准对此没有要求

2注意可能存在未定义的行为范围,包括忽略完全不可预测的结果,在翻译或程序执行期间以文档化的方式表现环境(无论是否发布诊断消息),终止翻译或执行(与发布诊断消息)。

3示例未定义行为的示例是整数溢出行为。

3.4.4 1 未指明的行为使用未指明的价值,或本国际标准提供两种或两种以上可能性并且在任何情况下都没有进一步要求的其他行为

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


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

实现定义的行为
该语言说我们有数据类型。 编译器供应商指定他们使用的尺寸,并提供他们所做的工作的文档。

未定义的行为
你做错了什么。 例如,你在int中有一个非常大的值,它不适合char 。 你怎么把这个值放在char ? 实际上没有办法! 任何事情都可能发生,但最明智的事情是将该int的第一个字节放入char 。 这样做分配第一个字节是错误的,但那就是发生了什么。

未指明的行为
这两个函数中的哪一个首先被执行?

void fun(int n, int m);

int fun1()
{
  cout << "fun1";
  return 1;
}
int fun2()
{
  cout << "fun2";
  return 2;
}
...
fun(fun1(), fun2()); // which one is executed first?

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


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

fun(fun1(), fun2()); 不是“实现定义”的行为? 毕竟编译器必须选择一个或另一个课程?

实现定义的和未指定的区别在于,编译器应该在第一种情况下选择一种行为,但不必在第二种情况下。 例如,一个实现必须有且只有一个sizeof(int)定义。 所以,不能说sizeof(int)对于程序的某个部分是4,对于其他部分是8。 与未指定的行为不同,在编译器可以说OK的地方,我将从左到右评估这些参数,并且从右到左评估下一个函数的参数。 它可能发生在同一个程序中,这就是为什么它被称为未指定的原因 。 事实上,如果指定了一些未指定的行为,C ++本来可以变得更容易。 看看Dr. Stroustrup的回答:

据称,给编译器提供这种自由并需要“普通的从左至右评估”的东西之间的差异可能很大。 我没有信服,但有了无数的编译器“利用自由”和一些激情捍卫自由的人,改变将是困难的,并且可能需要数十年才能渗透到C和C ++世界的遥远角落。 我很失望,并非所有的编译器都会对诸如++ i + i ++之类的代码发出警告。 类似地,参数的评估顺序是未指定的。

国际海事组织中有太多的“东西”是未定义的,未指定的,实现定义等。然而,这很容易说,甚至举出例子,但很难解决。 还应该注意的是,避免大部分问题并生成便携式代码并不是那么困难。

链接地址: http://www.djcxy.com/p/12723.html

上一篇: Undefined, unspecified and implementation

下一篇: What does a colon following a C++ constructor name do?