我知道未初始化的局部变量是未定义的行为(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++以外的任何语言中工作。我知道这个问题是关于c++的,但是如果你能写出好的c++代码和Java代码,这不是问题,那为什么不呢?也许有一天有人将不得不将其移植到其他语言,并且搜索像这样的“魔术”UB所导致的错误绝对是一场噩梦(特别是对于没有经验的C/ c++开发人员)。

这里有一个关于另一个类似UB的问题。想象一下,你试图在不知道这个UB的情况下找到这样的bug。如果你想阅读更多关于C/ c++中这些奇怪的东西,请阅读链接中的问题答案,并查看这个很棒的幻灯片。它将帮助你理解引擎盖下面是什么以及它是如何工作的;这不仅仅是另一个充满“魔力”的幻灯片。我确信即使是大多数有经验的C/c++程序员也能从中学到很多东西。

其他回答

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

#include <stdio.h>

int main() {

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

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

由于安全原因,必须清理分配给程序的新内存,否则信息可能会被使用,密码可能会从一个应用程序泄漏到另一个应用程序。只有在重用内存时,才会得到不同于0的值。很有可能,在堆栈上,前一个值是固定的,因为前一个内存的使用是固定的。

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

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

让我说清楚一点:我们不会在程序中调用未定义的行为。这从来都不是一个好主意,就这样。这条规则很少有例外;例如,如果您是实现offsetof的库实现者。如果您的情况属于这种例外,您可能已经知道这一点。在这种情况下,我们知道使用未初始化的自动变量是未定义的行为。

编译器对未定义行为的优化变得非常积极,我们可以发现许多未定义行为导致安全缺陷的情况。最臭名昭著的例子可能是Linux内核空指针检查删除,我在回答c++编译错误时提到过?围绕未定义行为的编译器优化将有限循环变成无限循环。

我们可以阅读CERT的危险优化和因果关系的损失(视频),其中说:

Increasingly, compiler writers are taking advantage of undefined behaviors in the C and C++ programming languages to improve optimizations. Frequently, these optimizations are interfering with the ability of developers to perform cause-effect analysis on their source code, that is, analyzing the dependence of downstream results on prior results. Consequently, these optimizations are eliminating causality in software and are increasing the probability of software faults, defects, and vulnerabilities.

特别是关于不确定值,C标准缺陷报告451:未初始化自动变量的不稳定性是一些有趣的阅读。它还没有解决,但引入了不稳定值的概念,这意味着值的不确定性可能在程序中传播,并且在程序的不同位置可能有不同的不确定值。

我不知道有什么例子会发生这种情况,但在这一点上,我们不能排除这种可能性。

真实的例子,而不是你期望的结果

你不太可能得到随机值。编译器可以优化整个循环。例如,用这个简化的例子:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r ;
    }
}

Clang优化了它(看现场):

updateEffect(int*):                     # @updateEffect(int*)
    retq

或者可能得到全0,就像这个修改后的情况:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r%255 ;
    }
}

现场观看:

updateEffect(int*):                     # @updateEffect(int*)
    xorps   %xmm0, %xmm0
    movups  %xmm0, 64(%rdi)
    movups  %xmm0, 48(%rdi)
    movups  %xmm0, 32(%rdi)
    movups  %xmm0, 16(%rdi)
    movups  %xmm0, (%rdi)
    retq

这两种情况都是完全可以接受的未定义行为形式。

注意,如果我们在一个Itanium上,我们可能会得到一个trap值:

[…]如果寄存器恰好保存了一个特殊的“not-a-thing”值, 读取寄存器陷阱,除了一些指令[…]

其他重要事项

有趣的是,在UB Canaries项目中,gcc和clang在利用未初始化内存的未定义行为方面存在差异。文章指出(重点是我的):

Of course we need to be completely clear with ourselves that any such expectation has nothing to do with the language standard and everything to do with what a particular compiler happens to do, either because the providers of that compiler are unwilling to exploit that UB or just because they have not gotten around to exploiting it yet. When no real guarantee from the compiler provider exists, we like to say that as-yet unexploited UBs are time bombs: they’re waiting to go off next month or next year when the compiler gets a bit more aggressive.

正如Matthieu M.指出的,每个C程序员应该知道的关于未定义行为#2/3也与这个问题有关。它说了很多(重点是我的):

The important and scary thing to realize is that just about any optimization based on undefined behavior can start being triggered on buggy code at any time in the future. Inlining, loop unrolling, memory promotion and other optimizations will keep getting better, and a significant part of their reason for existing is to expose secondary optimizations like the ones above. To me, this is deeply dissatisfying, partially because the compiler inevitably ends up getting blamed, but also because it means that huge bodies of C code are land mines just waiting to explode.

为了完整起见,我可能应该提到实现可以选择使未定义的行为定义良好,例如gcc允许类型双关语通过联合,而在c++中这似乎是未定义的行为。如果是这种情况,实现应该记录它,这通常是不可移植的。

好问题!

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

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

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

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

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