在C ++中使用随机性,我有这段代码:
#include <iostream>
// do not initialize return
int rand_int(){}
float rand_float(){}
int main(int argc, const char *argv[])
{
std::cout << rand_int() << std::endl; // Ok, Garbage value
std::cout << rand_float() << std::endl; // Always 0
return 0;
}
并且如评论中所示,有一种我不完全理解的行为。使用我的gcc 6.4(并且没有优化标志),rand_int
函数在每次调用时返回垃圾值(ok),但rand_float
似乎在每次调用时返回0。我真的不明白为什么,因为rand_float
函数没有初始化。
当执行流出声明为返回值的函数的末尾时,C ++标准没有定义行为(main
除外,这是特殊的)。因此,行为是特定计算平台上的事物的结果。
首先,应该注意的是,C ++标准未定义的任何行为都会在编译器优化期间发生变化。编译器可能会以激进的方式更改您的程序,即使编译器开关,源代码或其他因素稍有变化,更改也可能会有很大差异。
也就是说,处理器通常具有用于整数数据和浮点数据的单独寄存器。在您的进程进入main
例程之前,它会执行一些启动代码来准备堆栈和各种环境状态。当代码完成时,它会将其数据保留在寄存器中(因为它没有理由将其擦除),包括分配用于保存函数调用的返回值的寄存器。当您打印rand_int()
的结果时,您可能会看到该寄存器中剩余的一些数据。
启动代码可能没有理由使用浮点寄存器,因此它可能不会在其中放入任何数据。当您打印rand_float()
的结果时,您可能会看到零,因为操作系统在创建进程时将浮点寄存器初始化为零。
第一个未定义的行为只是意味着每个标准都没有定义行为,因此特定的实现可以做到它想要的,从即时程序崩溃到合理的处理。
我假设您的实现使用处理器累加器返回int
值,浮点协处理器1返回浮点值。由于累加器的使用有很多原因并且没有理由被隐瞒地保存,因此您可以预期垃圾或者更难以预测其中的数据。另一方面,您的实现似乎将浮点堆栈初始化为0并获得该值。简单地说,你不能依赖它:不同的编译器或相同编译器的不同版本甚至不同的编译选项可能导致不同的结果。甚至可能会发生在不同时间完全相同的编译会产生不同的结果......
TL / DR:UB意味着你可以得到任何东西......
这个功能签名:
int rand_int();
是广告合同。它说“我将返回一个int”。
该实现不会执行接口声明的操作。因此,您有未定义的行为,这意味着无法预测返回的值。实际上,不能假设返回的对象已经初始化。因此,调用此函数会导致整个程序的行为未定义。
有人可能会说这个明显的逻辑错误应该会产生编译器错误。我之前已经将这个案例提交给了iso-cpp邮件列表,就像其他人一样。奇怪(对我而言),相当多的c ++贡献者似乎不同意我的看法。
微软并不反对。在MSVC中,这是一个错误。