25、随机数生成:rand 和 srand 的正确用法

随机数,听起来很简单对吧?

很多新手写代码时,直接调个 rand() 就完事了。结果每次运行程序,出来的「随机数」都一样。嗯,这其实是个经典坑。我当年刚学C语言时也踩过,还一度怀疑自己电脑坏了。

rand() 到底有多「随机」?

说白了,rand() 生成的是伪随机数。它背后有一套固定的数学公式,每次调用都按这个公式算出一个数。如果你不给它一个「起点」,它每次都是从同一个起点开始算。那结果当然每次都一样。

这个起点,我们叫它「种子」。

核心结论:rand() 本身不随机,种子决定了随机序列。种子固定,序列就固定。

srand() 才是关键

srand() 就是用来设置种子的。你给它一个不同的种子,它就能生成不同的随机序列。

那问题来了:种子从哪来?

我个人的习惯是,用 time(NULL) 作为种子。因为时间每时每刻都在变,这样每次运行程序,种子都不一样。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    srand((unsigned)time(NULL));  // 用当前时间做种子
    for (int i = 0; i < 5; i++) {
        printf("%d\n", rand());
    }
    return 0;
}

这样每次运行,输出的5个数都不一样了。

小提示:srand() 只需要在程序开头调用一次。不要在循环里反复调用,否则反而会破坏随机性。

控制随机数的范围

实际项目中,我们很少直接要 rand() 返回的那个大数。通常我们需要一个特定范围内的随机数,比如 0~99,或者 1~100。

常用的写法是:

// 生成 0 到 (n-1) 之间的随机数
int r = rand() % n;

// 生成 1 到 n 之间的随机数
int r = rand() % n + 1;

// 生成 [a, b] 之间的随机数
int r = rand() % (b - a + 1) + a;

举个例子,生成 1~100 的随机数:

int score = rand() % 100 + 1;

注意:用取模的方式会有轻微的分布不均匀问题。如果对随机质量要求极高(比如加密、抽奖),建议用更专业的随机数库。但日常嵌入式开发中,这个写法完全够用。

一个完整的例子

我在项目中做过一个简单的猜数字小游戏,正好用到了随机数:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int target, guess;
    srand((unsigned)time(NULL));
    target = rand() % 100 + 1;  // 1~100

    printf("猜一个1~100之间的数字:\n");
    do {
        scanf("%d", &guess);
        if (guess > target) {
            printf("大了,再试试:\n");
        } else if (guess < target) {
            printf("小了,再试试:\n");
        } else {
            printf("恭喜你,猜对了!\n");
        }
    } while (guess != target);

    return 0;
}

每次运行,目标数字都不一样。这就是 srand() 的功劳。

常见错误与避坑指南

  • 忘记调用 srand():每次运行结果都一样,等于伪随机变成了「固定随机」。
  • 在循环里反复调用 srand():如果循环很快,time(NULL) 返回的值可能没变,导致生成相同的随机数。
  • 种子用固定值:比如 srand(42),那每次运行结果都一样。调试时可以这么用,但正式代码别这样。

我曾经在一个数据采集项目中,因为忘记调用 srand(),导致每次上电采集到的「随机噪声」完全一样。排查了半天,最后发现是种子没设。从那以后,我写任何用到随机数的代码,第一件事就是检查 srand() 有没有写。

知识体系图

下面这张图帮你理清 rand 和 srand 的关系:

rand() 与 srand() 核心逻辑 用户调用 srand(seed) 系统保存种子值 调用 rand() 返回伪随机数 关键点 种子相同 → 序列相同 种子不同 → 序列不同 常用种子:time(NULL) srand() 只需调用一次

总结一下

  • rand() 生成伪随机数,需要配合 srand() 设置种子。
  • srand() 在程序开头调用一次即可,常用 time(NULL) 做种子。
  • 用取模运算控制随机数范围,注意分布均匀性问题。
  • 调试时可以用固定种子,方便复现问题。

随机数这东西,看着简单,用不好就容易出 bug。你想想看,如果每次运行结果都一样,那还叫随机吗?


公众号:蓝海资料掘金营,微信 deep3321