【C/C++】如何生成随机数?带你深入了解rand函数

简介: 【C/C++】如何生成随机数?带你深入了解rand函数

目录


背景

rand()

srand()

时间戳

time()

完整源码


正文


背景


当我们学习完分支与循环并了解过一点点函数之后,就可以试着编写代码实现一个简易的猜数字小游戏了。


游戏方法:电脑生成一个1~100的随机数,玩家进行猜数字,


我们将游戏的实现封装在game()函数中,而第一步,就是让电脑生成一个随机数。


这里我们需要用到一个生成随机数的库函数rand。


rand()


image.png

看不懂没关系,我来为大家简单释义:


首先是rand函数的构造


int  rand  (void) //函数返回值为int类型,且无需参数


它的功能就是生成0~RAND_MAX(32767)的随机值。RAND_MAX其实就是32767。


然后使用rand函数之前,别忘了包含它所在的头文件stdlib.h。


这时我们来测试:

#include<stdio.h>
#include<stdlib.h>
void menu()
{
  printf("*********************************\n");
  printf("**********  1. play  ************\n");
  printf("**********  0. exit  ************\n");
  printf("*********************************\n");
}
//rand函数可以生成随机数
//返回一个0~RAND_MAX(32767)
void game()
{
  int guess = 0;
  //1. 生成随机数
  int ret = rand();
  printf("%d\n", ret);
}
int main()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("选择错误,重新选择!\n");
      break;
    }
  } while (input);


我们进行简单的测试后,发现一下结果:


13.png


随机数已经成功生成。


我们激动的关掉程序再来运行一次:


14.png


重新启动代码以后,我们观察这些数字是否有种似曾相识的感觉?


在不就是上次运行的结果吗?既然是随机数,那么完全有相同的可能。那我们不妨再多试几遍,


会发现,结果还是一样的。如果我们只是这样草率的完成游戏,玩家玩几次就会觉得无聊了,因为


每次都能猜中,那这是哪里出了问题呢?


请注意:

15.png

文档提示,使用rand函数之前,我们还得调用另外一个函数srand()


srand()


这是srand函数的构造:


void srand (unsigned int seed);


srand其实是一个随机数的生成器,当我们给它不同的参数,就能生成不一样的数字

例如:16.png


我们发现果真如此。但是只传一个固定的参数,我们发现还是不能完成任务,那么我们就能想


到,是不是给它一个一直变化的值就好了呢?确实是这样。


那么,在计算机中什么是一直变化的呢?答案是:时间。


我们需要将变化的时间作为srand的参数,但是我们不可能将年月日直接写进去吧。


这里我们了解一个概念叫时间戳。


时间戳


0.png


简单理解,计算机会把时间转换为一串数字进行存储,这串数字就叫时间戳(单位秒)。每个不同的时间都对应一个时间戳,每个时间戳也只对应一个时间。


因此我们把时间戳作为srand的参数即可,而获取时间戳,我们又得用到另外一个函数time().


time()


函数构造:

time_t    time    (time_t* timer);

我们发现time()需要的参数为指针类型的参数,由于目前没接触指针,我们只需要将NULL(空指针)作为参数即可。另外,别忘记包含头文件time.h。


整理一下:

这是game函数应该这样写:

void game()
{
  srand((unsigned int)time(NULL));//srand所需参数为unsigned int类型,这里用到了强制类型转换
  int guess = 0;
  //1. 生成随机数
  int ret = rand();
  printf("%d\n", ret);
}

我们再来测试:

00.png


这次已经得到我们想要的结果了。但是却并不完美,因为我们发现每次生成的随机数都很相近,其实,当我们手速足够快时,完全可以达到相同的结果:

000.png


这里的原因不难理解,计算机的时间是一秒一秒走的,所以转换出来的时间戳也是每次差一点。当我们在时间戳变换前多次获取随机值那么就会产生相同的结果。


其实,这里错在我们将srand函数放错位置了。每次程序运行时,我们只需要设置一次随机数的起点就够了,保证与下一局游戏数字不重复就行。


所以我们将srand的调用放置在main函数里,因为一个程序,main函数只会执行一次。


到此,我们在以后需要生成随机数时,只需要这套流程即可。


接下来,我们只需要继续完善游戏代码即可,这里就不做赘述。想了解游戏逻辑的小伙伴可以参照我的上一篇博客手撕三子棋。


完整源码


#define _CRT_SECURE_NO_DEPRECATE 1
// 猜数字游戏实现
//电脑随机生成一个1~100之间的数字
//猜数字
//如果你猜小了,告知猜小了
//如果你猜大了,告知猜大了
//如果你猜对了,告知猜对了
#include <stdlib.h>
#include <time.h>
void menu()
{
  printf("*********************************\n");
  printf("**********  1. play  ************\n");
  printf("**********  0. exit  ************\n");
  printf("*********************************\n");
}
//rand函数可以生成随机数
//返回一个0~RAND_MAX(32767)
void game()
{
  int guess = 0;
  //1. 生成随机数
  int ret = rand()%100+1;//0~99-->1~100
  //printf("%d\n", ret);
  //2. 猜数字
  while (1)
  {
    printf("请猜数字:>");
    scanf("%d", &guess);
    if (guess < ret)
    {
      printf("猜小了\n");
    }
    else if (guess > ret)
    {
      printf("猜大了\n");
    }
    else
    {
      printf("恭喜你,猜对了\n");
      break;
    }
  }
}
int main()
{
  int input = 0;
  //设置随机数的生成器
  srand((unsigned int)time(NULL));
  do
  {
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("退出游戏\n");
      break;
    default:
      printf("选择错误,重新选择!\n");
      break;
    }
  } while (input);
  return 0;
}
目录
相关文章
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
79 6
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
37 0
C++ 多线程之线程管理函数
|
2月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
38 3
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
351 1
|
2月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
48 1
|
2月前
|
安全 编译器 C++
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
24 3
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
60 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(二)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(三)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作