Rit*_*ose 5 c++ language-agnostic undefined-behavior
正如我从阅读中理解的那样,未定义的行为是编译器在编译时留下几个不同的替代品的结果.但是,这并不意味着如果要遵循严格的编码实践(比如将每个赋值和每个相等放在一个单独的语句中,进行适当的调试和注释)那么它在查找未定义的源代码时不会构成重大问题. -行为.
此外,对于每个出现的错误,如果您识别代码,您应该知道在该特定语句中可以使用哪些语句,对吗?
编辑:我对你编写的代码不感兴趣的地方不感兴趣.我对通过数学逻辑发声的代码无法工作的示例感兴趣.
此外,我认为"良好的编码实践"是每隔几行,适当的缩进和调试转储的强大信息性评论.
jal*_*alf 10
未定义的行为不一定会让编译器有多种选择.最常见的是它只是做一些没有意义的事情.
例如,请使用以下代码:
int arr[2];
arr[200] = 42;
Run Code Online (Sandbox Code Playgroud)
这是未定义的行为.并不是编译器有多种选择可供选择.只是我正在做的事情没有意义.理想情况下,首先不应该允许它,但是如果没有可能昂贵的运行时检查,我们无法保证在我们的代码中不会发生类似这样的事情.所以在C++中,规则只是语言只指定了遵守规则的程序的行为.如果它像上面的例子那样做了错误的事情,那么它就是未定义的应该发生的事情.
现在,想象一下你将如何检测这个错误.它是如何浮出水面的?它似乎永远不会导致任何问题.也许我们只是碰巧写入映射到进程的内存(所以我们没有得到访问冲突),但是从来没有使用过(因此程序的其他部分都不会读取我们的垃圾值,或者覆盖我们写的内容) ).然后它似乎是程序没有错误,并且工作得很好.
或者它可能会触及一个甚至没有映射到我们的进程的地址.然后程序会立即崩溃.
或者它可能会触及映射到我们进程的地址,但稍后会用于某些事情.然后我们所知道的是,从该地址读取的函数迟早会得到一个意想不到的值,它会表现得很奇怪.这部分很容易在调试器中找到,但它并没有告诉我们什么时候或从哪里写入垃圾值.因此,没有简单的方法可以将错误追溯到其源头.
小智 7
首先,来自C++ 03标准的一些定义:
1.3.5实现定义的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现和每个实现应记录
1.3.12未定义的行为
行为,例如可能在使用错误的程序结构或错误数据时出现,本国际标准不对其施加任何要求.当本国际标准忽略任何明确定义或行为的描述时,也可能预期未定义的行为.
1.3.13未指明的行为
行为,对于格式良好的程序构造和正确的数据,取决于实现.不需要实现来记录发生的行为.
即使未指定的行为可以被称为UB,我也从未见过,UB总是意味着未定义的行为. 在整个标准中,类似于"使X是未定义的行为"的语句,但有时你会碰到一个根本没有涵盖的案例.
以另一种方式定义,如果你在任何地方有任何未定义的行为,那么所有的赌注都是关闭的.就标准而言,您的计划可以做任何事情,从邀请您的岳母为SuperBowl周末到运行nethack.由于UB的本质,你不能测试它,你不能指望编译器的任何帮助.(虽然对于一些微不足道的常见错误,编译器通常会产生诊断.)
通常某些东西被定义为UB,因为它在逻辑上没有意义(例如,访问数组越界),但也经常因为它需要执行太多的工作来防止 - 通常是在运行时.记住C++源自C,能够生成高度优化的程序是两种语言的主要目标.为此,语言推迟程序员确保代码在这些情况下是正确的,与"你不为你不使用的东西付费"原则相关.
所以,最后,UB很糟糕,非常糟糕; 不惜一切代价避免它.然而,UB的困难部分不知道它是什么或在什么情况下发生; 当你调用UB时,困难的部分是识别.例如:
std::string s = "abc";
char& c = s[0];
cout.write(s.data(), s.length());
c = '-';
Run Code Online (Sandbox Code Playgroud)
看起来很合理吧?不,这是UB,但它会像你期望的那样在所有流行的实现上工作.
| 归档时间: |
|
| 查看次数: |
499 次 |
| 最近记录: |