2

我将从所使用术语的背景开始。

正确的

“在理论计算机科学中,当算法相对于规范正确时,算法的正确性就被断言了。” - 堆栈溢出的正确性标记。

不明确的

未定义的行为是允许任何事情发生的地方。实际上,可能发生的事情的可能性是无限的。示例是在 c++ 中取消引用 nullptr,并除以零。

定义明确

这是只有一个结果可能的地方。

实现定义

这是实现改变可能性数量的地方。如果某些实现定义的结果导致定义等同于未定义或明确定义,那么这不是我所指的。

未指定

这是存在大于一种但小于无限种可能性的地方。

利用未指定的行为

我指的是为了某些好处(但当然不限于)性能或程序的正确性,将您的程序开放给未指定的行为的想法。例如,将单线程程序转换为多线程程序可能会“利用”未指定的行为来谋取利益。

这个想法

这就是事情变得有趣的地方。或者,至少潜力是存在的。这是编程的灰色地带。有一些关于什么可以或不可以发生的想法,但是发生的事情既不是明确定义的,也不是未定义的。

一个简单且使用良好的示例将涉及多线程。在使用多个线程时,还有很多未知和不可知的地方。尽管如此,还是使用了多个线程,因为它带来了可能无法获得的主要性能提升。

在不牺牲正确性的情况下,有意识地实施未指定的行为还能带来哪些好处? 需要有一些好处。

对于那些可能想要结束这个想法的人,这将邀请基于意见的答案 - 我反驳说,基于专业知识要求答案的问题是一个例外,并且是允许的。这是我认为这个问题有效且可以接受的基础。这是一个可以回答的问题。

在具体参考我之前类似的问题时,我重新提问是因为我没有正确地表达这个问题。我已经对其进行了重新措辞,现在特别要求在不牺牲程序正确性的情况下显示好处的答案。

4

6 回答 6

3

这是一篇文章,展示了如何设计一种算法来容忍未初始化缓冲区中存在的不确定值,从而避免遍历缓冲区来初始化它(以及所有相应的缓存未命中)。

请注意,在 C++ 中,使用不确定值仅对unsigned char;是未指定的行为。对于所有其他类型,这将是未定义的行为。有关详细信息,请参阅标准的第 8.5 节。

于 2014-07-19T20:30:55.427 回答
2

未指定的行为不适合程序员使用(相反,如果程序员知道需要仔细查看和避免什么),未指定的行为,就像所有其他行为一样:未定义,实现定义等......是供编译器编写者利用的(例如,子表达式的评估顺序具有未指定的行为,因为您不应在评估中传递任何具有副作用的子表达式,并且假设您处理了这一点,编译器重新排序尽可能高效地评估子表达式,一些子表达式可以包含可以重用的相同计算,并且许多其他优化可以利用可以以它可以找到的最佳顺序进行评估的知识。

于 2014-07-19T19:27:14.560 回答
1

导致程序具有确定性行为的唯一情况是,无论实现使用哪种可能性,结果都是相同的。

一个简单的例子是函数调用的参数被评估的顺序。如果无论以哪种方式评估参数,结果的参数列表都是相同的,那么订单的未指定行为无关紧要。

于 2014-07-19T19:25:41.107 回答
0

我制作了一个“非常有用”的程序,它告诉您在运行程序时是先计算+表达式中的第一个操作数还是第二个操作数:foo() + bar()

#include <iostream>

bool done = false;

int foo() {
    if (!done) {
        std::cout << "First operand evaluated first" << std::endl;
        done = true;
    }
    return 0;
}

int bar() {
    if (!done) {
        std::cout << "Second operand evaluated first" << std::endl;
        done = true;
    }
    return 0;
}

int main()
{
    foo() + bar();
}

所以当然,您可以利用未指定的行为,但您可能不想这样做。在典型程序中,您希望未指定的行为解析为单个执行路径或允许对您的程序同样有效的多个执行路径。这在您链接到的问题中有所描述。

无论如何,所有未指定的行为都会带来好处。好处在于实现(并传递给开发人员和用户),因为它可以自由地执行优化(有时特定于特定机器)。

于 2014-07-19T19:26:57.693 回答
0

我不是一个聪明的火箭科学家。对我来说你的问题

1) ..对 C++ 没有意义

在 Stack Overflow 的大多数读者每天都在进行的传统编程中,我们只需要定义事物并以确定性和可预测的方式运行,从而产生有效的软件 - http://en.wikipedia.org/wiki /Deterministic_Turing_machine。如果它今天有效,我们希望它明天也有效

C++ 也属于这一类

在这样一个传统的世界中,我看不出对您的问题的回答实际上如何有用,因此这个问题可能如何有用

2) ..在面向硬件的语言中有意义

VHDL编程语言中,程序员使用(使用)特殊变量状态(参见What is the purpose of the `std_logic` enumerated type in VHDL?)表示一个不为人所知的信号值,即未连接承载信号的导线。切。拔下电源。白噪声

3) ..在传统 CPU 世界之外有意义

有一个完整的计算世界,其中未定义和未指定等术语完全发挥作用。它是http://en.wikipedia.org/wiki/Quantum_computer

谷歌投资于量子计算研究,从事这项工作的公司是D-Wave Systems Inc.。

但这确实是一种范式转变(类似于纳米计算机的编程)


所以我的平均智商答案要么是让你的问题对 C++ 有用,要么将它移到更合适的 Stack Exchange 站点(例如https://cs.stackexchange.com/ ),或者在 C++编程范式之外解渴

于 2014-07-20T06:03:25.030 回答
0

目前尚不清楚您要问的是哪两件事:

  1. 在标准表明编译器可以以任意方式从几种可能的行为中进行选择的情况下,但某些特定编译器总是使用相同的替代方案,用户代码是否可以有效地利用这种编译器的组成(尽管未指定)行为?

  2. 在标准允许编译器从几种可能的行为中进行选择的情况下,并且不同的行为会产生不同的结果,如果此类行为产生的任何可能的输出会影响其输出将受到未指定行为影响的程序,是否可以认为该程序是“正确的”符合要求?

我强烈警告不要依赖编译器对表达式不同部分的评估顺序的判断。如果一个循环展开,那么循环中的某些语句有时可能会以一种方式评估,有时会以另一种方式评估,这是完全合理的。虽然有时可能很方便地说int x = getc()+256*getc();,关于第一个或第二个字符是否会被相乘,缺乏任何一致性保证,这使得这种构造非常无用。

关于第二个问题,答案应该取决于行为的一致性是否属于程序的要求。如果一个程序需要产生满足某些标准的输出,并且每次运行产生任意相同或不同的输入是可以接受的,前提是每次运行产生满足标准的输出,那么它完全合法地选择输出取决于编译器如何实现一些未指定的行为。然而,在某些情况下,可能要求具有相同输入的程序多次运行必须产生相同的输出,即使要求对输出必须是什么的某些细节保持沉默。本质上,每次使用新输入运行程序时都会定义一个新需求。在那些情况下,

于 2015-07-27T21:52:20.823 回答