对于相同的输入,满足以下条件的代码是否可以为每次运行产生不同的输出?
- 该代码是单线程的,尽管它确实链接到一个线程安全的运行时库。
- 没有明确调用 rand() 或 time() 或它们的朋友。
- 有一些堆内存分配。
- 可能有一些(错误的)代码会导致未定义的行为。
对于相同的输入,满足以下条件的代码是否可以为每次运行产生不同的输出?
“未定义的行为”意味着任何事情都可能发生。这还包括每次运行程序时可能会发生不同的事情。
例如,如果您使用未初始化的内存,它可能与程序运行不同,程序运行该内存所包含的内容。
一个简单的例子:
int main() {
char s[1024];
s[1023] = '\0';
std::cout << s << std::endl;
}
这通常会在每次运行时打印一个不同的字符串。它不使用任何堆分配,我认为它甚至不是任何未定义的行为,所以它可能不是您问题的预期解决方案。
另一个例子是new
可以在每个程序运行时返回不同的地址(这里也没有 UB):
int main(void) {
std::cout << new int << std::endl;
}
因此,即使没有未定义的行为,也存在“随机性”的来源,因此对于未定义的行为,每个程序运行肯定也会发生不同的事情。
你会得到很多“未定义的行为会发生任何事情”的答案,所以我不会在这个话题上喋喋不休。我将假设您有一个不是您自己编写的程序,应该是确定性的,并且您必须对其进行调试,或者类似的东西。
现代操作系统具有地址随机化,因此使用地址作为整数的未定义行为可能是不确定的
返回的内存malloc()
不能保证归零,但操作系统通常通过在重用页面之前将页面归零来强制执行进程机密性。因此,当您malloc()
或使用堆栈时,您应该获得一个已归零的页面或您的进程之前自行填充的页面,因此不应引入不确定性。
这就是我现在能想到的。
看起来最后一个条件是这个问题的答案。除此之外,我可以考虑,例如,关于 time() 调用并在某些计算中使用它的结果。
您可能想阅读以下文章,了解为什么 未定义的行为会导致意外结果。
只是挑剔以下程序满足您的要求。
#include <time.h>
#include <iostream>
int main()
{
std::cout << time(NULL);
}
当然。
即使您不调用rand
or ,执行之间的许多事情仍然会有所不同time
。
例如:
rand
或time
在您不知情的情况下调用,当然,你的最后一点回答了它。如果你的代码包含未知的错误,你不能假设任何关于它的事情。如果其中一个错误是rand
即使您认为它不会调用,它也会调用怎么办?
最好不要试图在 UB 周围变得聪明。如果它是未定义的,那么它就是未定义的,如果你试图推断“在这种情况下它并没有那么糟糕”,那么你只是在为自己挖一个洞。因为它可能是。
除了其他人已经说过的话:
如果您的程序与用户交互,那么很可能会引入另一种随机性来源。用户可能会以不同的顺序或不同的时间引发不同的动作。根据这些操作是什么,程序内部可能会发生细微的差异(例如以不同的顺序分配内存)。
当您将其与各种形式的未定义行为(一个很好的例子是使用未初始化的内存)结合起来时,您最终可能会遇到每次运行程序时似乎都不同的症状,并且以看似不可预测的方式。