什么时候故意利用未指明的行为提供好处而不牺牲正确性?

时间:2014-07-19 18:51:23

标签: c++ performance theory correctness unspecified-behavior

我将从使用条款的背景开始。

正确

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

Undefined

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

良好定义

这是唯一可能的结果。

实施定义

这是实现改变可能性的地方。如果正在实现的内容导致定义等同于未定义或明确定义,那么它不是我所指的。

Unspecified

这是存在多于一个但少于无限多种可能性的地方。

利用未指定的行为

我所指的是将程序打开到未指定行为的想法,以获得某些好处,例如(但肯定不限于)性能或程序的正确性。例如,将单线程程序转换为多线程程序可能会使用"未明确的利益行为。

想法

这是事情变得有趣的地方。或者,至少潜力是存在的。这是编程的灰色区域。有一些想法可以或不可能发生,但所发生的事情既没有明确定义也没有定义。

一个简单且使用良好的例子涉及多线程。使用多个线程时,还有很多未知和不可知的问题。尽管如此,仍然使用多个线程,因为它带来了潜在的主要性能提升,否则将无法使用。

在没有牺牲正确性的情况下,故意实施未指定行为的其他方面还能提供哪些好处? 需要有一些好处。

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

In specific reference to my previous question along similar lines,我正在重新询问,因为我没有正确地说出这个问题。我已经重新措辞了,现在我特别需要一个答案,以显示利益而不牺牲程序的正确性。

6 个答案:

答案 0 :(得分:3)

Here's an article展示了如何设计一个容忍未初始化缓冲区中存在的不确定值的算法,从而避免迭代缓冲区来初始化它(以及所有相应的缓存未命中)。

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

答案 1 :(得分:2)

未指定的行为不是供程序员使用(相反,如果程序员知道它需要仔细查看和避免),未指定的行为,如所有其他行为:未定义,实现定义等...是为编译器编写者利用(例如子表达式的评估顺序具有未指定的行为,因为您不应在评估中传递任何具有副作用的子表达式,并假设你解决这个问题,编译器将子表达式的评估重新排序为更高效,一些子表达式可以包含可以重用的相同计算,而许多其他优化可以利用可以按照它能找到的最佳顺序进行评估的知识。

答案 2 :(得分:1)

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

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

答案 3 :(得分: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();
}

当然,您可以利用未指明的行为,但您可能不想这样做。在典型的程序中,您希望未指定的行为要么解析为单个执行路径,要么允许多个对您的程序同样有效的执行路径。这在the question you linked to中有描述。

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

答案 4 :(得分: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?)表示未知的信号值,因为未连接信号的导线。切。不插电。 White noise等。

3)..在传统的CPU世界之外感觉

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

Google invests in the quantum computing research和使用它的公司是D-Wave Systems Inc.

但这实际上是一种范式转换(类似于Nano computers的编程)


因此,我的平均智商答案要么是让你的问题对C ++有用,要么将它移到更合适的Stack Exchange站点(例如https://cs.stackexchange.com/)或者解除你对C ++之外的渴望programming paradigm

答案 5 :(得分:0)

目前还不清楚你要问的两件事中的哪一件:

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

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

  3. 我强烈建议不要依赖于编译器自由裁量权来评估表达式的不同部分的顺序。如果一个循环被展开,那么循环中的某些语句有时可能会以某种方式进行评估,有时甚至是另一种语句,这是完全可信的。虽然有时候能够说int x = getc()+256*getc();在第一个或第二个字符是否会成倍增加时缺乏任何一致性的保证可能会很方便,但这样的结构却毫无用处。

    关于第二个问题,答案应取决于行为的一致性是否符合计划的要求。如果要求一个程序产生符合某些标准的输出,并且如果每次运行产生任意相同或不同的输入是可以接受的,只要每次运行产生的输出符合标准,那么对于确切的选择是完全合理的。输出取决于编译器如何实现某些未指定的行为。但是,在某些情况下,可能要求具有相同输入的多个程序运行必须产生相同的输出,即使要求对输出必须是什么的某些细节保持沉默。基本上,每次使用新输入运行程序时都会定义新的需求。在这些情况下,依赖于未指定行为的输出将是不可取的。