C++ 非决定论的根源

C++ 非决定论的根源,c++,random,deterministic,non-deterministic,C++,Random,Deterministic,Non Deterministic,我的假定确定性程序在不同的运行中会产生几个稍有不同的输出之一。输入、编译器和计算机都是不变的。我不确定哪个输出是正确的,因为它看起来总是合理的 除了对rand()的错误调用之外,这怎么可能呢?可能是: 线程计时 任何类型的输入(用户、文件、网络等) 有几种方式: 以涉及 使用当前系统时间作为输入 使用未初始化的变量 我们当然可以做更多的猜测,但是如果您想得到有意义的帮助,也许发布代码的相关部分对您来说是件好事:-)而不查看一些代码(提示),我能想到的最好方法就是寻找一个模式。也许是特定日

我的假定确定性程序在不同的运行中会产生几个稍有不同的输出之一。输入、编译器和计算机都是不变的。我不确定哪个输出是正确的,因为它看起来总是合理的

除了对rand()的错误调用之外,这怎么可能呢?

可能是:

  • 线程计时
  • 任何类型的输入(用户、文件、网络等)
有几种方式:

  • 以涉及
  • 使用当前系统时间作为输入
  • 使用未初始化的变量
我们当然可以做更多的猜测,但是如果您想得到有意义的帮助,也许发布代码的相关部分对您来说是件好事:-)

而不查看一些代码(提示),我能想到的最好方法就是寻找一个模式。也许是特定日期和时间的事情


另外,试着寻找比赛条件。这可能看起来不确定。

如果您的输出取决于堆上分配的地址:

int main(int argc, char* argv[])
{
   printf("%p", malloc(42));
   return 0;
}

对于每次运行,malloc()可能会返回不同的虚拟地址,更不用说在分配失败的情况下返回NULL。

使用指针的值而不是指针指向的值总是会产生有趣的结果

除了一个偶然打给兰德的电话


rand()
是完全确定的,只要你给它注入相同的初始种子。

显然是一个新的bug实例。

  • 来自网络/互联网的输入
  • 日期/时间

在与“外部世界”互动不多的程序中,非确定性的流行来源是对指针比较的依赖。有时,您可能会在代码中看到它:当一个字典比较函数用完了要比较的东西(所有东西都是相等的)时,它会比较对象的地址,作为最后的手段。如果在动态内存中分配对象,这可能会产生不同的顺序,因为实际分配位置可能因平台和运行而异。

您没有提供太多信息。然而,作为一个以实时编程为生的人,当这些事情发生时,我寻找的最可能的罪魁祸首是:

  • 使用未初始化的内存
  • 比赛条件
  • 上述的一些模糊组合

例如,我曾经遇到过一个这样的问题,共享库没有我想象的那么“共享”,并且试图使用一个进程的句柄来索引第二个进程中尚未初始化的表。取决于事情的启动方式,可能导致也可能没有导致第三个进程中的重要数据被破坏。

任何未定义的行为。i、 e:要解释输出更改的每一个可能来源需要数百页的篇幅。尝试调试发现更改发生在哪里,或者读取一些C++规范.< /p> 如果程序使用浮点/双,如果在某些架构上有上下文切换,结果可能会有差异。 在x86上,FPU对中间结果使用扩展精度,但当保存在内存中时(当存在进程或线程上下文切换时会发生这种情况),这种精度将丢失。这可能会导致结果出现一些小的差异(我们在程序中检测到了这样的问题)。避免此问题的一种方法是要求编译器不要使用FPU,而是使用SSE进行浮点操作


我们需要先看一些代码,然后再进行疯狂的推测。您的代码中是否包含任何误入未定义行为领域的内容?谁说编译器是确定性的?@AshleysBrain-请看这个问题@Andy_Vulhop-以前从未阻止过任何人,但我同意我们会更高效地查看代码。很好。堆/堆栈地址随机化是当今相当标准的特性。如果指针被用作某个地方的键,这将产生影响。我曾多次使用它来稳定排序(如果键相等,则比较指针)。不过,您必须非常不幸/在内存非常低的情况下运行才能发生分配失败。如果堆分配的对象按地址排序,则输出将取决于堆上分配的地址,例如,如果指向它们的指针存储在一个有序的容器中,就像一个集合;我们需要密码!当然,所有这些因素以及十进制错误和伪随机在技术上都是确定性的,因为它们在确定性机器上运行。在个人控制较少的分时系统的背景下,当我们无法跟踪或理解程序的输入和数以百万计的渐进状态时,它们对我们来说似乎是不确定的。这就是为什么理论和计算机科学总是和基本编程技能一起重要的原因。我应该明确地说,我只想要一个常见可能性的列表,这就是我得到的。顺便说一句,除非附加调试器,否则未初始化的变量实际上是确定性的;然而,它们是确定性的,就像单线程堆损坏是确定性的一样,这不是您想要的确定性。