未定义,未指定和实现定义的行为

未定义,未指定和实现定义的行为

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



RISEBY
浏览 1151回答 5
5回答

萧十郎

未定义的行为是C和C ++语言的一个方面,对于来自其他语言的程序员来说可能会令人惊讶(其他语言试图更好地隐藏它)。基本上,有可能编写不能以可预测的方式运行的C ++程序,即使许多C ++编译器不会报告程序中的任何错误!让我们看一个经典的例子:#include&nbsp;<iostream>int&nbsp;main(){ &nbsp;&nbsp;&nbsp;&nbsp;char*&nbsp;p&nbsp;=&nbsp;"hello!\n";&nbsp;&nbsp;&nbsp;//&nbsp;yes&nbsp;I&nbsp;know,&nbsp;deprecated&nbsp;conversion &nbsp;&nbsp;&nbsp;&nbsp;p[0]&nbsp;=&nbsp;'y'; &nbsp;&nbsp;&nbsp;&nbsp;p[5]&nbsp;=&nbsp;'w'; &nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;p;}变量p指向字符串文字"hello!\n",下面的两个赋值尝试修改该字符串文字。这个程序做什么用的?根据C ++标准的第2.14.5节第11段,它调用未定义的行为:尝试修改字符串文字的效果是未定义的。我可以听到人们尖叫“但是等等,我可以编译这个没问题并得到输出yellow”或“你的意思是什么未定义,字符串文字存储在只读内存中,所以第一次分配尝试会导致核心转储”。这正是未定义行为的问题。基本上,一旦你调用未定义的行为(甚至是鼻子恶魔),标准允许任何事情发生。如果根据您的语言心理模型存在“正确”行为,那么该模型就是错误的;&nbsp;C ++标准有唯一的投票期。未定义行为的其他例子包括访问超出其边界的数组,解引用空指针,访问对象后,他们的寿命结束或写入据说聪明的表情一样i++ + ++i。C ++标准的第1.9节还提到了未定义行为的两个不那么危险的兄弟,未指定的行为和实现定义的行为:本国际标准中的语义描述定义了参数化的非确定性抽象机器。抽象机的某些方面和操作在本国际标准中描述为实现定义的(例如,sizeof(int))。这些构成了抽象机器的参数。每个实施应包括描述其在这些方面的特征和行为的文件。抽象机器的某些其他方面和操作在本国际标准中被描述为未指定的(例如,对函数的参数的评估顺序)。在可能的情况下,本国际标准定义了一组允许的行为。这些定义了抽象机器的非确定性方面。本国际标准中将某些其他操作描述为未定义(例如,取消引用空指针的效果)。[&nbsp;注意:本国际标准对包含未定义行为的程序的行为没有要求。-&nbsp;结束说明&nbsp;]具体而言,第1.3.24节规定:允许的未定义行为包括完全忽略不可预测的结果,在翻译或程序执行期间以环境特征(有或没有发出诊断消息)的特定行为,终止翻译或执行(发布时)一条诊断信息)。你能做些什么来避免遇到未定义的行为?基本上,你必须阅读那些了解他们所谈论内容的作者的优秀C ++书籍。螺丝网络教程。螺旋公牛队。

森栏

也许简单的措辞可以比标准的严格定义更容易理解。实现定义的行为语言表示我们有数据类型。编译器供应商指定他们使用的大小,并提供他们所做的文档。未定义的行为你做错了什么。例如,您有一个非常大的值int,不适合char。你怎么把这个价值放进去char?实际上没有办法!任何事情都可能发生,但最明智的做法是将该int的第一个字节放入其中char。分配第一个字节是错误的,但这就是幕后发生的事情。未指定的行为首先执行这两个函数?void&nbsp;fun(int&nbsp;n,&nbsp;int&nbsp;m);int&nbsp;fun1(){ &nbsp;&nbsp;cout&nbsp;<<&nbsp;"fun1"; &nbsp;&nbsp;return&nbsp;1;}int&nbsp;fun2(){ &nbsp;&nbsp;cout&nbsp;<<&nbsp;"fun2"; &nbsp;&nbsp;return&nbsp;2;}...fun(fun1(),&nbsp;fun2());&nbsp;//&nbsp;which&nbsp;one&nbsp;is&nbsp;executed&nbsp;first?该语言未指定评估,从左到右或从右到左!因此,未指定的行为可能会或可能不会导致未定义的行为,但当然您的程序不应产生未指定的行为。

慕哥9229398

for&nbsp;fun(fun1(), fun2());是不是“实现定义”的行为?毕竟编译器必须选择一个或另一个课程?实现定义和未定义之间的区别在于编译器应该在第一种情况下选择一种行为,但在第二种情况下不需要。例如,实现必须只有一个定义sizeof(int)。因此,它不能说sizeof(int)程序的某些部分为4,其他部分为8。与未指定的行为不同,编译器可以说OK,我将从左到右评估这些参数,并且从右到左评估下一个函数的参数。它可能发生在同一个程序中,这就是为什么它被称为未指定的原因。实际上,如果指定了一些未指定的行为,C ++可能会变得更容易。看看Stroustrup博士对此的回答:据称,为编制者提供这种自由并要求“普通的从左到右的评估”可以产生的差异可能很大。我不相信,但是有无数的编译器“在那里”利用自由和一些人热情地捍卫自由,改变将是困难的,可能需要数十年才能渗透到C和C ++世界的遥远角落。我很失望并非所有编译器都会对++ i + i ++等代码发出警告。同样,参数的评估顺序是未指定的。IMO太多“事物”未定义,未指定,实现定义等。但是,这很容易说,甚至可以提供示例,但很难修复。还应该注意,避免大多数问题并产生可移植代码并不是那么困难。

慕盖茨4494581

来自官方C理由文件术语未指定的行为,未定义的行为和实现定义的行为用于对编写程序的结果进行分类,这些程序的属性标准不能或不能完全描述。采用这种分类的目的是允许实现中的某种变化,这允许实现的质量成为市场中的主动力量以及允许某些流行的扩展,而不去除与标准的一致性的标记。标准的附录F对属于这三个类别之一的行为进行了编目。未指定的行为使实现者在翻译程序时具有一定的自由度。只要没有翻译程序,这个范围就不会延伸。未定义的行为使实现者许可证不会捕获难以诊断的某些程序错误。它还标识了可能符合语言扩展的区域:实现者可以通过提供正式未定义行为的定义来扩充语言。实现定义的行为使实现者可以自由选择适当的方法,但需要向用户解释此选择。指定为实现定义的行为通常是用户可以基于实现定义做出有意义的编码决策的行为。在决定实施定义应该有多广泛时,实施者应该牢记这个标准。与未指定的行为一样,只是无法转换包含实现定义的行为的源不是一个充分的响应。
打开App,查看更多内容
随时随地看视频慕课网APP