9

既然C语言未初始化的局部变量值不确定,那么能否使用它做“随机值”呢?

 3 years ago
source link: https://blog.popkx.com/%E6%97%A2%E7%84%B6c%E8%AF%AD%E8%A8%80%E6%9C%AA%E5%88%9D%E5%A7%8B%E5%8C%96%E7%9A%84%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E5%80%BC%E4%B8%8D%E7%A1%AE%E5%AE%9A-%E9%82%A3%E4%B9%88%E8%83%BD%E5%90%A6/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

既然C语言未初始化的局部变量值不确定,那么能否使用它做“随机值”呢?

发表于 2019-08-28 20:08:38   |   已被 访问: 417 次   |   分类于:   C语言   |   暂无评论

在C语言程序中,未初始化的局部变量值是未定义(UB,undefined behaviour)的,使用未初始化的变量可能会为程序带来意想不到的错误,这一点看过我文章的读者应该都是清楚的。

但是,反过来想一想,“未定义的”也就意味着未初始化的局部变量内部的值是不确定的,那么它能不能被当作“随机值”使用呢?例如下面这段C语言代码:

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

程序仅仅把未初始化的局部变量 r, g, b, isVisible 当作随机值使用,并未在其他逻辑中使用,这样做看起来要比调用 rand() 函数产生随机值效率高多了:

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);
    }
}
8c67314f6f93a88e67d8bdb7f63deb0b.png

真的如此吗?

首先应该明白,计算机语言是绝对不能有歧义的,因此为C语言程序引入“未定义”的行为无疑是有违这一原则的。另外,使用未初始化局部变量作为随机值,不太可能具有理想的数学统计特性。

例如在 x86(_ 64) 架构中,从未初始化的寄存器中确实会读取出不能事先预知的值,但是这样的值并不能当作是“随机值”,因为局部变量的值存储在栈中,未初始化的值其实是栈中对应位置上一次使用者留下的。

所以如果该栈区一直没有被使用过,那么即使未初始化的局部变量也会每次都有相同的值,这显然不符合预期——产生“随机值”。换句话说,希望未初始化的局部变量提供“随机值”实在是不可靠,甚至指望它每次提供不一样的值都需要依靠运气。

即使在某段C语言代码中,未初始化的局部变量能够每次都提供不同的值,它们的数学统计特性也是不可靠的,我们不能指望它符合某种分布。

与此同时,引入“未定义”的行为到我们的C语言代码中永远是不值得推荐的做法。程序员永远都不该写不安全的代码,引入的例外越少,最终的程序就越容易避免意外错误。

因此,在C语言程序开发中,如果希望得到一个随机数,那么通过 rand() 函数获取就是最好的选择。基本上也不用担心它的效率,因为基于 rand() 函数的随机数生成器只不过是一个乘法运算,一个加法运算和一个模运算而已。

我所知道的最快的生成器是使用 uint32_t 类型作为随机变量,并且通过下面这个表达式计算:

I = 1664525 * I + 1013904223;

I 的初始值即为所谓的随机数种子,这个表达式可以产生连续的随机数。为了尽量得到高效率的随机数生成器,内联函数和函数式宏定义都是不错的选择。

上面表达式里的两个常数,是由著名的科学程序员 Donald Knuth 手工挑选的。

事实上,随机性是程序开发中非常具体,又非常难以获得的属性。把一些难以追踪的(未定义)数值当作随机值,是一种常见的错误。

阅读更多:   C语言


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK