我知道未初始化的局部变量是未定义的行为(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域时,任何事情都可能发生(包括守护进程从你鼻子上飞出去),通常意味着编译器作者不会关心,对于局部变量,其值将是当时堆栈内存中的任何值。

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

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

其他回答

不,太糟糕了。

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

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

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

I = 1664525 * I + 1013904223

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

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

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

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

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

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

好问题!

未定义并不意味着它是随机的。考虑一下,在全局未初始化变量中获得的值是由系统或您的/其他应用程序运行时遗留在那里的。根据您的系统对不再使用的内存的处理和/或系统和应用程序生成的值类型,您可能会得到:

总是一样的。 成为一小部分价值观中的一员。 获取一个或多个小范围内的值。 在16/32/64位系统的指针上看到许多可以被2/4/8整除的值 ...

您将得到的值完全取决于系统和/或应用程序留下的非随机值。因此,确实会有一些噪音(除非您的系统删除不再使用的内存),但您将从中提取的值池绝不是随机的。

对于局部变量,情况会变得更糟,因为它们直接来自您自己程序的堆栈。在执行其他代码期间,您的程序很有可能实际编写这些堆栈位置。我估计在这种情况下运气的机会非常低,而你所做的“随机”代码更改将尝试这种运气。

阅读随机性。正如你所看到的,随机性是一种非常特殊且难以获得的属性。一个常见的错误是,如果你只是采取一些难以跟踪的东西(比如你的建议),你会得到一个随机的值。

正如其他人所注意到的,这就是未定义行为(UB)。

实际上,它(可能)实际上(有点)管用。在x86[-64]架构上读取未初始化的寄存器确实会产生垃圾结果,而且可能不会做任何坏事(与例如Itanium相反,那里的寄存器可以被标记为无效,因此读取会传播NaN之类的错误)。

但这里存在两个主要问题:

It won't be particularly random. In this case, you're reading from the stack, so you'll get whatever was there previously. Which might be effectively random, completely structured, the password you entered ten minutes ago, or your grandmother's cookie recipe. It's Bad (capital 'B') practice to let things like this creep into your code. Technically, the compiler could insert reformat_hdd(); every time you read an undefined variable. It won't, but you shouldn't do it anyway. Don't do unsafe things. The fewer exceptions you make, the safer you are from accidental mistakes all the time.

UB更紧迫的问题是它使整个程序的行为没有定义。现代编译器可以使用它来省略大量的代码,甚至可以回溯到过去。玩UB就像维多利亚时代的工程师拆除一个活跃的核反应堆。有无数的事情会出错,而且您可能连一半的基本原则或实现的技术都不知道。这可能没什么,但你仍然不应该让它发生。看看其他漂亮的答案来了解细节。

还有,我会炒了你。