我知道未初始化的局部变量是未定义的行为(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);
    }
}

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


当前回答

未定义的行为是未定义的。这并不意味着你得到了一个未定义的值,这意味着程序可以做任何事情,并且仍然满足语言规范。

一个好的优化编译器应该

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);
    }
}

并编译成noop。这当然比任何其他选择都要快。它的缺点是什么都做不了,但这就是未定义行为的缺点。

其他回答

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

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

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

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

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

我也不会解雇你。

我喜欢你的思维方式。真的是跳出了框框。然而,这种权衡真的不值得。内存和运行时的权衡是一个问题,但运行时的未定义行为却不是。

知道自己使用如此“随机”的业务逻辑,一定会让您感到非常不安。我不会那么做的。

不,太糟糕了。

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

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

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

I = 1664525 * I + 1013904223

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

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

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

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

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

我做了一个非常简单的测试,它根本不是随机的。

#include <stdio.h>

int main() {

    int a;
    printf("%d\n", a);
    return 0;
}

每次我运行程序,它都输出相同的数字(在我的例子中是32767)——没有比这更随机的了。这大概是运行时库中留在堆栈上的启动代码。由于每次程序运行时都使用相同的启动代码,并且在两次运行之间程序中没有其他变化,因此结果是完全一致的。