程序状态的任何变化是否构成可观察的行为?
考虑以下两个程序:
程序一
int main()
{
printf( "hello\n" );
}
程序二
int main()
{
srand( 0 );
if( rand() ) {
printf( "hello\n" );
} else {
printf( "hello\n" );
}
}
它们是否具有相同的可观察行为?根据 C++ 标准 (1.9/6),可观察的行为包括:
- 读取和写入
易失性
- 数据库 I/O 函数
现在 srand()
和 rand()
可能不是 I/O 函数(尽管我不知道给定的实现是否使用某些硬件噪声源),但它们会修改程序内部状态。他们会操纵易失性
数据吗?我不知道。对 printf()
的调用显然是 I/O 操作,并且其序列在两个程序中是相同的。
上面的两个程序是否具有相同的可观察行为?我如何知道两个给定的程序是否具有相同的可观察行为?
Consider the two following programs:
program one
int main()
{
printf( "hello\n" );
}
program two
int main()
{
srand( 0 );
if( rand() ) {
printf( "hello\n" );
} else {
printf( "hello\n" );
}
}
Do they have the same observable behavior or not? According to C++ Standard (1.9/6) the observable behavior includes:
- reads and writes to
volatile
data - library I/O functions
Now srand()
and rand()
are likely not I/O functions (although I have no idea whether a given implementation uses some hardware noise source), but they modify program internal state. Do they manipulate volatile
data? I don't know. Calls to printf()
are clearly I/O operations and sequences thereof are identical in both programs.
Do the two programs above have the same observable behavior? How do I know if two given programs have the same observable behavior?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您所说,这取决于 srand() 和 rand() 是否具有可观察到的副作用。他们不应该;不能有外部噪声源,因为对于给定的种子,序列需要可重复,并且没有其他原因执行 I/O 或访问易失性数据。
如果编译器能够确定它们不存在(例如,如果它们是在标头中内联定义的,或者链接器足够智能以执行额外的优化),那么它将能够忽略它们;否则,它必须假设它们确实存在,并将它们包括在内。在许多实现中,这些函数将位于预编译库中,并且链接器不会那么智能,因此您最终将调用这些函数;然而,一个好的编译器应该注意到
if
的两个分支是相同的并省略测试。(更新:如评论中所述,只有当编译器可以确定未来可观察的行为不能依赖于其副作用时,才可以省略对 rand() 的调用)。
一般来说,这是一个非常困难的问题,并且有些程序是无法分辨的(原因与停止问题类似)。对于像这样简单的程序,你可以简单地列出可观察的操作并比较它们;如果行为以一种不平凡的方式依赖于程序的输入,那么这很快就会变得非常困难。
As you say, that depends on whether
srand()
andrand()
have observable side effects. They shouldn't; there can be no external noise source, since the sequence is required to be repeatable for a given seed, and there is no other reason to perform I/O or access volatile data.If the compiler is able to determine that they don't (e.g. if they are defined inline in a header, or the linker is smart enough to perform extra optimisations), then it would be able to omit them; otherwise, it must assume that they do, and include them. In many implementations, these functions will be in a precompiled library, and the linker won't be that smart, so you will end up calling the functions; however, a decent compiler should notice that both branches of the
if
are identical and omit the test.(UPDATE: as noted in the comments, the call to
rand()
can also only be omitted if the compiler can determine that no future observable behaviour can depend on its side effects).In general, that's a very difficult problem, and there will be some programs where it's impossible to tell (for reasons similar to the Halting Problem). For programs as simple as this, you can simply list the observable operations and compare them; if the behaviour depends on the program's input in a non-trivial way, then that soon gets very difficult to do.