第一部分:理解 rand() 函数
rand() 是 C 标准库 <stdlib.h> 中的一个函数,用于生成一个伪随机整数。

基本用法
rand() 函数不接受任何参数,它返回一个 int 类型的值,这个值的范围通常是 0 到 RAND_MAX 之间。
RAND_MAX 是在 <stdlib.h> 中定义的一个宏,它代表了 rand() 函数能返回的最大值,在不同的系统中,RAND_MAX 的值可能不同,但至少是 32767。
最简单的例子:
#include <stdio.h>
#include <stdlib.h> // 包含 rand() 和 RAND_MAX
int main() {
// 生成并打印一个随机数
int random_number = rand();
printf("一个随机数: %d\n", random_number);
// 打印 RAND_MAX 的值
printf("RAND_MAX 的值是: %d\n", RAND_MAX);
return 0;
}
问题: 如果你运行上面的代码几次,你会发现它每次输出的“随机数”都是一样的!
这是因为 rand() 使用一个种子来生成序列,如果种子不变,序列就不变。

设置随机种子:srand()
为了让 rand() 每次运行程序时都产生不同的序列,我们需要在调用 rand() 之前,用 srand() 函数来设置一个不同的种子。
srand(seed) 函数接受一个 unsigned int 类型的参数作为种子。
如何获得一个变化的种子?
最好的方法是使用当前的时间,因为时间总是在变化的,C 标准库 <time.h> 中的 time(NULL) 函数可以返回自某个固定时间点(称为“纪元”)以来的秒数。
正确的用法:

#include <stdio.h>
#include <stdlib.h> // rand(), srand(), RAND_MAX
#include <time.h> // time()
int main() {
// 1. 设置随机种子
// 只需要在程序开始时设置一次即可
// time(NULL) 返回当前时间,将其作为种子
srand(time(NULL));
// 2. 生成随机数
int random_number = rand();
printf("使用时间种子后的随机数: %d\n", random_number);
return 0;
}
每次运行这个程序,你都会得到不同的随机数。
第二部分:生成特定范围的随机数
rand() 生成的是 0 到 RAND_MAX 的数,但我们通常需要的是特定范围的,1 到 100。
生成 [0, N-1] 范围内的随机数
使用取模运算符 。
// 生成一个 0 到 99 之间的随机数 int number = rand() % 100;
原理: rand() % N 的结果总是在 0 到 N-1 之间。
生成 [1, N] 范围内的随机数
在 [0, N-1] 的基础上加 1。
// 生成一个 1 到 100 之间的随机数 int number = (rand() % 100) + 1;
生成 [M, N] 范围内的随机数
通用公式:rand() % (N - M + 1) + M
解释:
N - M + 1计算出这个范围的总数(10 到 20,总数是 11)。rand() % (N - M + 1)生成[0, 总数-1]的数。+ M将这个范围平移到[M, N]。
例子:生成 50 到 100 之间的随机数
int min = 50; int max = 100; int range = max - min + 1; // 100 - 50 + 1 = 51 int number = (rand() % range) + min; // 结果在 0~50 之间,加上 50 后就是 50~100
第三部分:实践案例 - “猜数字”游戏
这个案例完美地诠释了如何让“计算机”来生成一个数字,然后让用户来猜,这里的“计算机”就是我们的随机数生成器。
游戏规则:
- 计算机随机生成一个 1 到 100 之间的整数。
- 用户输入一个猜测的数字。
- 计算机告诉用户是“猜大了”、“猜小了”还是“猜对了”。
- 如果猜错,继续猜测,直到猜对为止。
完整代码:
#include <stdio.h>
#include <stdlib.h> // rand(), srand()
#include <time.h> // time()
int main() {
int secret_number, guess, attempts = 0;
// 1. 计算机生成秘密数字
// 设置随机种子,确保每次游戏数字都不同
srand(time(NULL));
// 生成 1 到 100 之间的随机数
secret_number = (rand() % 100) + 1;
printf("欢迎来到猜数字游戏!\n");
printf("我已经想好了一个 1 到 100 之间的整数,\n");
// 2. 循环,让用户进行猜测
do {
printf("请输入你的猜测: ");
scanf("%d", &guess);
attempts++; // 增加猜测次数
if (guess > secret_number) {
printf("猜大了!\n");
} else if (guess < secret_number) {
printf("猜小了!\n");
} else {
printf("恭喜你!猜对了!\n");
printf("你总共猜了 %d 次,\n", attempts);
}
} while (guess != secret_number); // 直到猜对才退出循环
return 0;
}
代码解析:
srand(time(NULL));:这是游戏的关键,它确保了计算机每次“想”的数字都是不同的。secret_number = (rand() % 100) + 1;:这就是计算机的核心,它生成了 1 到 100 的随机数。do-while循环:让用户可以一直猜,直到猜对为止。scanf("%d", &guess);:获取用户的输入。if-else if-else结构:计算机根据用户的猜测给出反馈。
第四部分:进阶 - 提升随机数质量
rand() 函数生成的随机数在统计上可能不够“随机”,对于一些简单的游戏或程序来说足够了,但对于科学计算、密码学等需要高质量随机数的场景,它就不够用了。
更好的随机数生成器:C++11 <random> (C语言中不常用)
如果你使用的是 C++,强烈推荐使用 <random> 库中的 std::mt19937 等现代随机数引擎,它们产生的随机数质量远高于 rand()。
C11 标准中的 rand_r()
在 C11 标准中,引入了 rand_r(),它是一个可重入的版本,比 rand() 更安全,可以在多线程环境中使用,它需要一个指向种子的指针。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
unsigned int seed = time(NULL);
// rand_r 接受一个指向种子的指针
printf("使用 rand_r 生成的随机数: %d\n", rand_r(&seed));
return 0;
}
| 函数/概念 | 作用 | 头文件 | 示例 |
|---|---|---|---|
rand() |
生成一个伪随机整数 | <stdlib.h> |
int r = rand(); |
RAND_MAX |
rand() 能返回的最大值 |
<stdlib.h> |
printf("%d", RAND_MAX); |
srand(seed) |
设置 rand() 的随机种子 |
<stdlib.h> |
srand(time(NULL)); |
time(NULL) |
获取当前时间,常用于 srand 的种子 |
<time.h> |
srand(time(NULL)); |
rand() % N |
生成 [0, N-1] 范围的随机数 |
<stdlib.h> |
int dice = rand() % 6; // 0-5 |
(rand() % N) + 1 |
生成 [1, N] 范围的随机数 |
<stdlib.h> |
int num = (rand() % 100) + 1; // 1-100 |
核心要点:
- 必须用
srand(time(NULL))来初始化随机种子,否则每次运行结果都一样。 - 只在程序开始时调用一次
srand()。 - 使用 取模 和 加减法 来控制随机数的范围。
- “计算机”生成随机数的过程,就是调用
rand()函数并根据需求处理其返回值的过程。
