我知道未初始化的局部变量是未定义的行为(UB),而且值可能有陷阱表示,这可能会影响进一步的操作,但有时我想使用随机数仅为视觉表示,而不会在程序的其他部分进一步使用它们,例如,在视觉效果中设置随机颜色的东西,例如:

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

比那么快吗

void updateEffect(){
    for(int i=0;i<1000;i++){
        star[i].setColor(rand()%255,rand()%255,rand()%255);
        star[i].setVisible(rand()%2==0?true:false);
    }
}

也比其他随机数生成器快吗?


当前回答

未定义的行为意味着编译器的作者可以自由地忽略这个问题,因为无论发生什么,程序员都没有权利抱怨。

理论上,当进入UB域时,任何事情都可能发生(包括守护进程从你鼻子上飞出去),通常意味着编译器作者不会关心,对于局部变量,其值将是当时堆栈内存中的任何值。

这也意味着内容通常是“奇怪的”,但是是固定的,或者是稍微随机的,或者是可变的,但是有一个清晰的模式(例如,在每次迭代中增加值)。

当然,你不能指望它是一个不错的随机生成器。

其他回答

如果操作得当,使用未初始化的数据来获得随机性并不一定是件坏事。事实上,OpenSSL正是这样做的,以播种它的PRNG。

显然,这种用法并没有很好地记录下来,因为有人注意到Valgrind抱怨使用未初始化的数据,并“修复”了它,导致了PRNG中的一个错误。

所以你可以这样做,但你需要知道你在做什么,并确保任何阅读你的代码的人都理解这一点。

在任何想要使用未初始化变量的地方使用7757。我从质数列表中随机选择了它:

这是被定义的行为 它保证不总是0 它是质数 它很可能在统计上是随机的,就像未初始化一样 变量 它可能比未初始化的变量快,因为它的 值在编译时已知

还有一种可能性需要考虑。

现代编译器(嗯,g++)非常聪明,它们会检查你的代码,看看哪些指令影响状态,哪些不影响状态,如果一条指令被保证不影响状态,g++会简单地删除那条指令。

接下来会发生什么。g++肯定会看到你正在读取,执行算术运算,保存,本质上是一个垃圾值,这会产生更多的垃圾。因为不能保证新的垃圾会比旧的垃圾更有用,所以它只会让你的循环消失。杂音!

这个方法很有用,但下面是我要做的。结合UB(未定义行为)与rand()速度。

当然,reduce rand()被执行了,但是把它们混合在一起,这样编译器就不会做任何你不希望它做的事情。

我也不会解雇你。

不,太糟糕了。

使用未初始化变量的行为在C和c++中都是未定义的,而且这样的方案不太可能具有理想的统计属性。

如果你想要一个“快速而肮脏”的随机数生成器,那么rand()是你最好的选择。在它的实现中,它所做的只是一个乘法、一个加法和一个模数。

我所知道的最快的生成器需要你使用uint32_t作为伪随机变量I的类型,并使用

I = 1664525 * I + 1013904223

生成连续的值。你可以选择任何你喜欢的I的初始值(称为种子)。显然你可以内联编码。无符号类型的标准保证包装充当模数。(数字常数是由杰出的科学程序员Donald Knuth精心挑选的。)

未定义的行为意味着编译器的作者可以自由地忽略这个问题,因为无论发生什么,程序员都没有权利抱怨。

理论上,当进入UB域时,任何事情都可能发生(包括守护进程从你鼻子上飞出去),通常意味着编译器作者不会关心,对于局部变量,其值将是当时堆栈内存中的任何值。

这也意味着内容通常是“奇怪的”,但是是固定的,或者是稍微随机的,或者是可变的,但是有一个清晰的模式(例如,在每次迭代中增加值)。

当然,你不能指望它是一个不错的随机生成器。