random 函数并不是C语言标准库的一部分,它是 BSD Unix 系统(以及许多类Unix系统,如 macOS)中的一个扩展函数,在标准C(C89/C90, C99, C11等)中,对应的随机数函数是 rand()。

random 函数的行为和用法会因操作系统而异,下面我将分别介绍 random 函数(在支持的系统上)和标准C的 rand() 函数,并对它们进行比较。
random 函数 (BSD/SysV 扩展)
random 函数通常被认为是比标准 rand() 更高质量的随机数生成器,因为它通常使用更好的算法(在BSD系统中是 arc4random,或者基于线性同余法的改进版本)。
函数原型
long random(void);
- 功能:返回一个从
0到RAND_MAX之间的伪随机长整型数。 - 头文件:
<stdlib.h>
关键特性
- 更好的随机性:
random通常比rand()产生更高质量的随机数序列,周期更长,分布更均匀。 - 自动播种:这是
random与rand()最大的区别之一。random函数在程序首次调用时会自动使用一个随机的种子(通常是/dev/urandom或其他系统熵源),从而避免了rand()中常见的忘记调用srand()导致每次运行都产生相同序列的问题。 - 无状态:
random函数本身不维护状态,它依赖于一个“随机数发生器”的状态,你需要使用srandom()来播种,然后用random()来获取值。
相关函数
为了使用 random,你通常会用到一组配套的函数:
| 函数 | 原型 | 功能 |
|---|---|---|
srandom |
void srandom(unsigned int seed); |
初始化随机数发生器(播种),如果你希望每次运行程序时都得到相同的随机序列(例如用于调试),可以手动调用它。 |
initstate |
char *initstate(unsigned int seed, char *state, size_t size); |
更高级的用法,允许你为随机数发生器分配内存并指定状态缓冲区的大小。size 越大,随机性越好。 |
setstate |
char *setstate(char *state); |
切换到之前由 initstate 保存的状态。 |
使用示例
在支持 random 的系统(如 Linux, macOS)上:

#include <stdio.h>
#include <stdlib.h> // 包含 random 和 srandom 的头文件
int main() {
// 1. 播种 (可选)
// 如果你希望每次运行都得到不同的序列,可以不调用 srandom(),它会自动播种。
// 如果你希望得到可重复的序列,则手动调用。
// srandom(time(NULL));
// 2. 生成并打印5个随机数
printf("5 random numbers using random():\n");
for (int i = 0; i < 5; i++) {
long r = random();
printf("%ld\n", r);
}
return 0;
}
注意:在Linux上,random() 实际上可能是一个宏,指向 glibc 的 random_r,但其接口和概念是相似的。
标准C的 rand() 函数
rand() 是C标准库中定义的随机数函数,在所有符合标准的C编译器上都可以使用。
函数原型
int rand(void);
- 功能:返回一个从
0到RAND_MAX之间的伪随机整型数。 - 头文件:
<stdlib.h> RAND_MAX:是一个宏,定义在<stdlib.h>中,其值至少是 32767(即2^15 - 1),但通常是更大的值,如 2147483647。
关键特性
- 需要手动播种:
rand()函数在程序启动后,其内部种子(一个初始值)是固定的(通常是1),如果你不调用srand(),那么每次运行程序,rand()生成的随机数序列都将是完全相同的,这是初学者最容易犯的错误。 - 基于线性同余法:
rand()通常使用一个简单的线性同余算法来生成随机数,这个算法速度快,但随机性质量不高,序列较短且可能存在相关性。
相关函数
| 函数 | 原型 | 功能 |
|---|---|---|
srand |
void srand(unsigned int seed); |
初始化 rand() 使用的随机数种子。必须在第一次调用 rand() 之前调用。 |
RAND_MAX |
宏 | int 类型的最大值,rand() 能返回的最大值。 |
使用示例
#include <stdio.h>
#include <stdlib.h> // 包含 rand 和 srand 的头文件
#include <time.h> // 包含 time 函数的头文件
int main() {
// 1. 播种 - 使用当前时间作为种子
// time(NULL) 返回自1970年1月1日以来的秒数,确保每次运行种子都不同
srand((unsigned int)time(NULL));
// 2. 生成并打印5个随机数
printf("5 random numbers using rand():\n");
for (int i = 0; i < 5; i++) {
int r = rand();
printf("%d\n", r);
}
return 0;
}
random vs. rand() 核心区别
| 特性 | random() (BSD) |
rand() (标准C) |
|---|---|---|
| 标准性 | 非标准,是BSD/SysV扩展 | 标准C函数,所有平台都支持 |
| 返回类型 | long |
int |
| 播种 | 自动 (可选手动 srandom) |
必须手动 (srand) |
| 随机质量 | 通常更高,周期更长,分布更均匀 | 一般,算法简单,随机性较差 |
| 平台兼容性 | Linux, macOS, BSD等 | 所有C编译器 |
如何生成特定范围的随机数?
无论是 random 还是 rand,它们生成的数都是 [0, RAND_MAX] 或 [0, LONG_MAX] 范围内的,实际应用中,我们通常需要某个特定范围的随机数,1 到 100。
错误的做法:rand() % N
// 错误!会导致分布不均 int r = rand() % 100; // 生成 0 到 99
问题:RAND_MAX 不是 N 的整数倍,那么序列前面的几个数字出现的概率会比后面的数字高,如果 RAND_MAX 是 32767,32767 % 100 是 67,那么数字 0 到 67 出现的概率就比 68 到 99 高。
正确的做法:使用 rand() / RAND_MAX
// 正确!分布均匀 int r = (int)(rand() / (RAND_MAX + 1.0) * 100); // 生成 0 到 99 // 如果要生成 1 到 100 int r = (int)(rand() / (RAND_MAX + 1.0) * 100) + 1;
原理:rand() / (RAND_MAX + 1.0) 会生成一个 [0.0, 1.0) 区间内的小数,乘以 N 就得到了 [0.0, N) 区间内的数,再强制转换为整数,就得到了 [0, N-1] 的均匀分布。
对于 random() 的做法
原理完全相同,只是类型和宏名不同:
// 生成 1 到 100 long r = (long)(random() / ((double)LONG_MAX + 1.0) * 100) + 1;
在实际编程中,RAND_MAX 和 LONG_MAX 的具体值可能未知,但上述方法保证了数学上的正确性。
总结与建议
-
可移植性优先:如果你的代码需要在所有C编译器上运行(特别是Windows上的MSVC),必须使用
rand()和srand(),这是最安全、最标准的选择。 -
质量优先且平台可控:如果你的代码主要运行在Linux、macOS等类Unix系统上,并且对随机数质量有要求(如游戏、模拟、密码学等),优先使用
random(),它能提供更好的随机性,并且自动播种省去了麻烦。 -
C++11及以后:如果你使用的是C++,强烈建议使用C++11引入的
<random>库,它提供了多种高质量的随机数引擎(如std::mt19937)和分布器(如std::uniform_int_distribution),功能强大且易于使用,是现代C++中生成随机数的最佳实践。 -
加密安全:绝对不要使用
rand()或random()来生成密码、密钥或任何与安全相关的随机数,这些函数是伪随机的,其结果是可预测的,对于加密安全的需求,应使用操作系统提供的专门接口,- Linux/Unix:
/dev/random或/dev/urandom - Windows:
BCryptGenRandom或CryptGenRandom - macOS:
SecRandomCopyBytes
- Linux/Unix:
