C语言完全听懂系列 - day6

  • 标准库提供了一个rand()函数,每次调用它,都会生成一个随机数。这次我们除了#include <stdio.h>之外,还需要引入stdlib,lib是library,库的缩写。标准库的代码根据功能的不同,分别放在了不同的文件里,比如输入输出相关的在stdio.h,时间相关的在time.h,而stdlib.h放的就是一些“通用”的函数。
  • 我们生成两个随机数并输出:
#include <stdio.h>
#include <stdlib.h>

int main() {
    int random_number = rand();
    int random_number2 = rand();
    printf("%d %d", random_number, random_number2);
}
  • 运行一下,可以看到控制台打印了两个随机数。如果你重新运行,很奇怪的是,他会重新生成和上次一样的随机数。为什么?
  • 在计算机的世界,所有的事情都是确定的,从一个确定的位置开始,经历一系列固定不变的步骤,申请指定大小的内存存储数据辅助计算,最后得到一个确定的结果,一切都是确定好的,能推演出来的。就算用户输入有变化也无所谓,程序通常会用if else来概括出所有可能的情况,然后写好在每一种情况下,该做什么。这就是一台确定的机器,一切都是已知的。而“随机”代表的是一个未来的,未知的结果,计算机做不到这种事,它生成不了真正的随机数,它会连续编一串看起来毫不相关的数字,然后告诉你这是随机生成的,它生成的永远是同一串数字,伪随机数。就像圆周率?每次取下一位,看起来都和之前的没什么关系,但是整体来看,每一个位置是什么,都是确定好的。
  • 如果我不想让他每次都生成同一组数字怎么办?rand()生成随机数时,需要一个种子,种子可以理解为一个初始值,没有设置时默认的初始值是数字1,随机数本身则是用这个初始值作为参数,按照某种固定的算法计算得出,相同的种子会得到相同的随机数序列,不同的种子会得到不同的随机数序列。
  • 所以,我们每次设置不同的种子就可以模拟出更像随机数的效果了:
#include <stdio.h>
#include <stdlib.h>

int main() {
    int seed;
    scanf("%d", &seed);
    srand(seed);
    int random_number = rand();
    int random_number2 = rand();
    printf("%d %d", random_number, random_number2);
}
//运行两次,输入输出如下
//2
//
//45 29216
//3
//
//48 7196
  • srand()用于给rand()指定种子,s是seed的意思。用户输入是未知的,每次运行时,由用户输入传入不同的种子,就可以得到类似于真随机的结果。可是这样很麻烦,我们需要找到另一个每次运行的时候都不一样的值,而且得是自动变化的,不需要输入的,这个值通常会用当前时间。
    • 闲聊:很难说现实世界到底算不算真随机,可能也是设置了某个初始值(种子)之后,生成的一个确定的,可推演的,按时间推移的状态序列呢。现在是过去的必然结果,未来是现在的必然结果,一切早在最开始就已经是命中注定。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    time_t current_time = time(NULL);
    srand(current_time);
    int random_number = rand();
    int random_number2 = rand();
    printf("%d %d", random_number, random_number2);
}
  • 引入time.h库的time()函数,他的返回值表示自 1970 年 1 月 1 日 00:00:00 UTC这一时刻起,到现在经过了多少秒,UTC是0时区的意思,英国格林尼治天文台所在的时区,我们东八区是UTC+8。就像之前说的,在计算机的世界里,一切都是确定的,时间的起点也应该是确定的,这个起点就约定在了这一时刻。我现在运行时,time()返回的是1750681028,表示从起点开始,到现在为止,经历了这么多秒。他需要一个参数NULL,涉及到了后面的知识,暂时先记住必须要传一个参数NULL吧。另外我们定义了一个time_t类型的变量current_time,这个time_t类型就像intfloat一样,也是一种数据类型,只不过是专门用来存储时间的,_t是type的缩写。
    • 我有时候真受不了这帮老登的起名习惯,喜欢加一个字母的前缀或者后缀,然后我还得去研究是什么意思。
    • 扩展:1970 年 1 月 1 日 00:00:00 UTC和1970 年 1 月 1 日 08:00:00 UTC+8,是同一个时间点的两种不同表示,或者说是同一时刻。时间只有加上时区才是无歧义的。
    • 扩展:time_t这个类型,底层其实就是整数类型,想一下他的定义,从开始到现在过了多少秒,他一定是一个数字,而且不会出现小数值,所以底层就是整型,也就是intint占4个字节,他能存储的数值范围大概是从-21亿~21亿,所以他能表示的时间范围也是有限的,当这个数字增长到能表示的最大值时,再之后的时间就无法正确表示了。这个时间点大概在2038年。也就是说,到了2038年的某一时刻,会有一大批电脑和手机的时间会失效。为了解决这个问题,现代操作系统都升级了time_t的底层类型,换成了8字节的long long类型,这样就可以表示到三千亿年之后,比地球寿命长,应该暂时够用了。这里存在一个兼容性问题,部分操作系统升级到了8字节,但是一些设备可能还没升级,我们的代码如果希望能在两种操作系统上都能正常运行怎么办?所以老登们定义了一个自动化的time_t类型,用来存储时间,他会自动根据你的操作系统来决定底层用哪种,你写代码的时候就不需要关心这个问题了。
  • 如果想生成指定范围的随机数,比如说[1, 100]区间内的数值,可以写成这样:int num = rand() % 100 + 1;rand()默认会生成大于等于0,小于某个系统设置的值的,这个值通常很大很大,具体是多少不重要。rand % 100就是[0, 99],再加1就是[1, 100]
posted @ 2025-06-23 21:16  merlbc  阅读(34)  评论(0)    收藏  举报