C 语言 rand 与 srand
更新时间:2026-08-26。本文是
languages/c/主题入门层第 71 篇。猜数字游戏、洗牌、蒙特卡洛模拟、A/B 测试——都要"随机数"。C 标准库的rand用起来简单,但不播种的话每次运行结果一模一样,这是新手第一个翻车现场。本篇讲清播种、取区间、以及rand的局限。
本文要回答的问题
- 为什么每次都生成一样的数?srand 怎么用?
- 怎么生成指定范围的随机数?
- rand 有什么局限?什么时候该换更好的?
一、rand:伪随机数
c
#include <stdlib.h>
#include <stdio.h>
for (int i = 0; i < 5; i++)
printf("%d ", rand()); // 每次运行结果都一样!rand() 返回 0 到 RAND_MAX(通常是 32767)之间的整数。它是伪随机的:基于一个种子,按算法生成看似随机的序列——种子相同,序列就相同。
"每次运行都一样":因为默认种子是 1。同一序列第一次跑新鲜,第二次跑就是"预言家"了。
二、srand:播种
c
#include <stdlib.h>
#include <time.h>
srand(time(NULL)); // 用当前时间做种子(最常用)c
srand((unsigned)time(NULL));
for (int i = 0; i < 5; i++)
printf("%d ", rand()); // 每次运行不同播种规则:程序里播种一次(通常 main 开头),不要循环里反复 srand——同一秒内播同一种子,序列还一样。
三、生成指定区间:取模陷阱
要 [0, n) 的整数,新手直接 rand() % n——有均匀性缺陷:当 RAND_MAX+1 不能被 n 整除时,余数小的区间概率略高。
c
// 一般场景可以接受:
int r = rand() % 100; // 0..99
// 对均匀性要求高时:
int r = rand() / (RAND_MAX / 100 + 1); // 区间分配更均匀范围 [min, max] 的标准写法:
c
int roll_die(void) {
return rand() % 6 + 1; // 1..6
}
int rand_range(int min, int max) {
return min + rand() % (max - min + 1); // min..max
}浮点随机:
c
double r = (double)rand() / RAND_MAX; // 0.0..1.0四、rand 的局限
| 局限 | 说明 |
|---|---|
| 周期短 | RAND_MAX 通常 32767,质量一般 |
| 可预测 | 知道种子和算法就能复现 |
| 不适合加密 | 密码学必须用 CSPRNG |
| 不同平台行为不同 | RAND_MAX 可能不同 |
什么时候换更好的:
- 安全性敏感(token、密码):用
/dev/urandom、getrandom(Linux)或 OpenSSL 的随机; - 科学计算/模拟:用
drand48、PCG、MT19937; - 游戏/测试等一般用途:
rand够用。
五、实战:猜数字的核心
c
srand((unsigned)time(NULL));
int secret = rand() % 100 + 1; // 1..100完整的猜数字游戏就在下一篇。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 不播种 | 每次一样 | main 开头 srand 一次 |
| 循环里反复播种 | 序列重复 | 只播一次 |
| 取模均匀性 | 分布略偏 | rand()/(RAND_MAX/n+1) |
| 用 rand 做加密 | 可被预测 | 换 CSPRNG |
| 忘 #include <time.h> | 编译警告 | 头文件带上 |
七、与本站主线衔接
- 完整实践,见猜数字游戏;
- 随机性与蒙特卡洛模拟进阶,见随机数与模拟;
- 时间作为种子来源,见time.h 时间处理。
一句话总结
rand() 生成 0~RAND_MAX 的伪随机数,默认种子固定所以不播种每次结果一样;程序开头 srand(time(NULL)) 播一次种、min + rand() % (max-min+1) 取区间(对均匀性敏感时避开直接取模)——rand 是"够用但不完美",娱乐测试随便用,安全场景必须换 CSPRNG。