一文詳解如何使用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字游戲
1. 引言
在前面的學(xué)習(xí)中,我們已經(jīng)掌握了C語(yǔ)言的分支和循環(huán)語(yǔ)句。今天,我們將把這些知識(shí)綜合運(yùn)用起來(lái),編寫一個(gè)非常經(jīng)典且有趣的小項(xiàng)目——猜數(shù)字游戲。
通過(guò)這個(gè)項(xiàng)目,你將深入理解:
- 如何生成隨機(jī)數(shù)
- 如何結(jié)合循環(huán)和分支控制游戲邏輯
- 如何設(shè)計(jì)一個(gè)完整的程序結(jié)構(gòu)
這個(gè)游戲雖然簡(jiǎn)單,但卻是面試中經(jīng)常被問(wèn)到的邏輯題,也是你從語(yǔ)法學(xué)習(xí)邁向項(xiàng)目實(shí)戰(zhàn)的第一步。
2. 游戲需求分析
在開始寫代碼之前,我們先明確一下游戲規(guī)則:
- 電腦自動(dòng)生成一個(gè)1~100之間的隨機(jī)數(shù)
- 玩家猜數(shù)字,在猜的過(guò)程中,程序會(huì)根據(jù)玩家猜測(cè)的數(shù)據(jù)大小給出 “大了” 或 “小了” 的反饋
- 直到玩家猜對(duì),游戲結(jié)束
這個(gè)需求看似簡(jiǎn)單,但實(shí)現(xiàn)它的核心在于:如何讓電腦生成一個(gè)真正的隨機(jī)數(shù)?
3. 隨機(jī)數(shù)生成
要想完成猜數(shù)字游戲,首先得產(chǎn)生隨機(jī)數(shù)。C語(yǔ)言為我們提供了幾個(gè)相關(guān)的函數(shù)。
3.1rand函數(shù)
C語(yǔ)言提供了一個(gè)叫 rand 的函數(shù),它可以生成隨機(jī)數(shù),函數(shù)原型如下:
int rand (void);
rand函數(shù)會(huì)返回一個(gè)偽隨機(jī)數(shù),范圍在0 ~ RAND_MAX之間RAND_MAX的大小依賴編譯器實(shí)現(xiàn),大部分編譯器上是 32767- 使用
rand函數(shù)需要包含頭文件:stdlib.h
我們來(lái)測(cè)試一下 rand 函數(shù),多調(diào)用幾次,產(chǎn)生5個(gè)隨機(jī)數(shù):
#include <stdio.h>
#include <stdlib.h>
int main()
{
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
運(yùn)行結(jié)果分析:
我們先運(yùn)行一次,看看結(jié)果,再運(yùn)行一次再看看結(jié)果,多運(yùn)行幾次會(huì)發(fā)現(xiàn):
- 第一次運(yùn)行結(jié)果:41 18467 6334 26500 19169
- 第二次運(yùn)行結(jié)果:41 18467 6334 26500 19169
雖然一次運(yùn)行中產(chǎn)生的5個(gè)數(shù)字是相對(duì)隨機(jī)的,但是下一次運(yùn)行程序生成的結(jié)果和上一次一模一樣,這就說(shuō)明有點(diǎn)問(wèn)題。
知識(shí)點(diǎn): 其實(shí) rand 函數(shù)生成的隨機(jī)數(shù)是偽隨機(jī)數(shù)。偽隨機(jī)數(shù)不是真正的隨機(jī)數(shù),是通過(guò)某種算法生成的隨機(jī)數(shù)。真正的隨機(jī)數(shù)是無(wú)法預(yù)測(cè)下一個(gè)值是多少的。而 rand 函數(shù)是對(duì)一個(gè)叫 “種子” 的基準(zhǔn)值進(jìn)行運(yùn)算生成的隨機(jī)數(shù)。
之所以前面每次運(yùn)行程序產(chǎn)生的隨機(jī)數(shù)序列是一樣的,那是因?yàn)?rand 函數(shù)生成隨機(jī)數(shù)的默認(rèn)種子是1。如果要生成不同的隨機(jī)數(shù),就要讓種子是變化的。
3.2srand函數(shù)
C語(yǔ)言中又提供了一個(gè)函數(shù)叫 srand,用來(lái)初始化隨機(jī)數(shù)的生成器,srand 的原型如下:
void srand (unsigned int seed);
程序中在調(diào)用 rand 函數(shù)之前先調(diào)用 srand 函數(shù),通過(guò) srand 函數(shù)的參數(shù) seed 來(lái)設(shè)置 rand 函數(shù)生成隨機(jī)數(shù)時(shí)的種子,只要種子在變化,每次生成的隨機(jī)數(shù)序列也就變化起來(lái)了。
那也就是說(shuō)給 srand 的種子如果是隨機(jī)的,rand 就能生成隨機(jī)數(shù);但在生成隨機(jī)數(shù)的時(shí)候又需要一個(gè)隨機(jī)數(shù),這就矛盾了。
3.3time函數(shù)
在程序中我們一般是使用程序運(yùn)行的時(shí)間作為種子的,因?yàn)闀r(shí)間時(shí)刻在發(fā)生變化。
C語(yǔ)言中有一個(gè)函數(shù)叫 time,就可以獲得這個(gè)時(shí)間,time 函數(shù)原型如下:
time_t time (time_t* timer);
time函數(shù)會(huì)返回當(dāng)前的日歷時(shí)間,其實(shí)返回的是 1970年1月1日0時(shí)0分0秒 到現(xiàn)在程序運(yùn)行時(shí)間之間的差值,單位是秒- 返回的類型是
time_t類型的,time_t類型本質(zhì)上其實(shí)就是32位或者64位的整型類型 time函數(shù)的參數(shù)timer如果是非NULL的指針的話,函數(shù)也會(huì)將這個(gè)返回的差值放在timer指向的內(nèi)存中帶回去- 如果
timer是 NULL,就只返回這個(gè)時(shí)間的差值。time函數(shù)返回的這個(gè)時(shí)間差也被叫做:時(shí)間戳
使用 time 函數(shù)的時(shí)候需要包含頭文件:time.h
// VS2022 上 time_t 類型的說(shuō)明
#ifndef _CRT_NO_TIME_T
#ifdef _USE_32BIT_TIME_T
typedef __time32_t time_t;
#else
typedef __time64_t time_t;
#endif
#endif
typedef long __time32_t;
typedef __int64 __time64_t;
如果只是讓 time 函數(shù)返回時(shí)間戳,我們就可以這樣寫:
time(NULL); // 調(diào)用time函數(shù)返回時(shí)間戳,這里沒有接收返回值
3.4 組合使用:生成真正的隨機(jī)數(shù)
現(xiàn)在,我們可以將 srand 和 time 組合起來(lái),讓 rand 生成真正的隨機(jī)數(shù):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
// 使用time函數(shù)的返回值設(shè)置種子
// 因?yàn)閟rand的參數(shù)是unsigned int類型,我們將time函數(shù)的返回值強(qiáng)制類型轉(zhuǎn)換
srand((unsigned int)time(NULL));
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
printf("%d\n", rand());
return 0;
}
多運(yùn)行幾次看看,每次的運(yùn)行就有差異了。
注意: srand 函數(shù)是不需要頻繁調(diào)用的,一次運(yùn)行的程序中調(diào)用一次就夠了。
3.5 設(shè)置隨機(jī)數(shù)的范圍
在實(shí)際開發(fā)中,我們往往需要生成指定范圍內(nèi)的隨機(jī)數(shù)。下面是一些常用的技巧:
| 目標(biāo)范圍 | 代碼寫法 | 原理說(shuō)明 |
|---|---|---|
| 0 ~ 99 | rand() % 100 | 余數(shù)的范圍是0~99 |
| 1 ~ 100 | rand() % 100 + 1 | %100的余數(shù)是099,+1后范圍是1100 |
| 100 ~ 200 | 100 + rand() % (200-100+1) | 余數(shù)的范圍是0100,加100后就是100200 |
通用公式: 要生成 a ~ b 的隨機(jī)數(shù),方法如下:
a + rand() % (b - a + 1)
4. 猜數(shù)字游戲?qū)崿F(xiàn)
掌握了隨機(jī)數(shù)的生成方法,現(xiàn)在我們來(lái)完整實(shí)現(xiàn)猜數(shù)字游戲。
4.1 基礎(chǔ)版本
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{
int r = rand() % 100 + 1;
int guess = 0;
while (1)
{
printf("請(qǐng)猜數(shù)字>:");
scanf("%d", &guess);
if (guess < r)
{
printf("猜小了\n");
}
else if (guess > r)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜對(duì)了\n");
break;
}
}
}
void menu()
{
printf("***********************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("***********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請(qǐng)選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戲結(jié)束\n");
break;
default:
printf("選擇錯(cuò)誤,重新選擇\n");
break;
}
} while (input);
return 0;
}
4.2 進(jìn)階版本:增加次數(shù)限制
為了讓游戲更有挑戰(zhàn)性,我們可以加上猜數(shù)字的次數(shù)限制,如果5次猜不出來(lái),就算失敗。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void game()
{
int r = rand() % 100 + 1;
int guess = 0;
int count = 5;
while (count)
{
printf("\n你還有%d次機(jī)會(huì)\n", count);
printf("請(qǐng)猜數(shù)字>:");
scanf("%d", &guess);
if (guess < r)
{
printf("猜小了\n");
}
else if (guess > r)
{
printf("猜大了\n");
}
else
{
printf("恭喜你,猜對(duì)了\n");
break;
}
count--;
}
if (count == 0)
{
printf("你失敗了,正確值是:%d\n", r);
}
}
void menu()
{
printf("***********************\n");
printf("****** 1. play ******\n");
printf("****** 0. exit ******\n");
printf("***********************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("請(qǐng)選擇:>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("游戲結(jié)束\n");
break;
default:
printf("選擇錯(cuò)誤,重新選擇\n");
break;
}
} while (input);
return 0;
}
5. 代碼結(jié)構(gòu)分析
讓我們來(lái)梳理一下這個(gè)程序的整體結(jié)構(gòu):
5.1 函數(shù)模塊化設(shè)計(jì)
| 函數(shù)名 | 功能 | 關(guān)鍵點(diǎn) |
|---|---|---|
menu() | 打印游戲菜單 | 純輸出,無(wú)參數(shù)無(wú)返回值 |
game() | 核心游戲邏輯 | 生成隨機(jī)數(shù)、循環(huán)判斷、次數(shù)控制 |
main() | 程序入口 | 初始化隨機(jī)種子、菜單循環(huán)、分支選擇 |
5.2 程序執(zhí)行流程

6. 面試常見問(wèn)題與解答
Q1:rand()和srand()的關(guān)系是什么?
解答: rand() 用于生成偽隨機(jī)數(shù),srand() 用于設(shè)置隨機(jī)數(shù)生成器的種子。如果不調(diào)用 srand(),rand() 默認(rèn)使用種子1,導(dǎo)致每次程序運(yùn)行生成的隨機(jī)數(shù)序列相同。通過(guò) srand() 設(shè)置不同的種子(通常使用 time(NULL) 返回的時(shí)間戳),可以讓 rand() 每次生成不同的隨機(jī)數(shù)序列。
Q2:為什么使用time(NULL)作為srand()的參數(shù)?
解答: time(NULL) 返回當(dāng)前時(shí)間的時(shí)間戳(從1970年1月1日到現(xiàn)在的秒數(shù)),這個(gè)值每秒鐘都在變化。用它作為種子,可以確保每次運(yùn)行程序時(shí)種子都不同,從而生成不同的隨機(jī)數(shù)序列。這是最簡(jiǎn)單且有效的獲取變化種子的方法。
Q3:如何生成指定范圍 [a, b] 的隨機(jī)數(shù)?
解答: 使用公式 a + rand() % (b - a + 1)。原理是:rand() % (b - a + 1) 生成 0 到 (b - a) 之間的隨機(jī)數(shù),再加上 a,范圍就變成了 a 到 b。例如生成1~100的隨機(jī)數(shù):1 + rand() % 100。
Q4:猜數(shù)字游戲中,do-while循環(huán)和while循環(huán)哪個(gè)更合適?為什么?
解答: do-while 循環(huán)更合適。因?yàn)橛螒虿藛沃辽傩枰@示一次讓用戶選擇,do-while 保證循環(huán)體至少執(zhí)行一次,符合“先顯示菜單,再判斷是否繼續(xù)”的邏輯。如果使用 while 循環(huán),需要在循環(huán)前額外顯示一次菜單,代碼會(huì)顯得冗余。
Q5:在進(jìn)階版本中,如何實(shí)現(xiàn)“5次猜不對(duì)就失敗”的邏輯?
解答: 使用一個(gè)計(jì)數(shù)器 count 初始化為5。每次循環(huán) count--,當(dāng) count 變?yōu)?時(shí)循環(huán)結(jié)束。循環(huán)結(jié)束后判斷 count == 0,如果是則說(shuō)明用戶沒有在5次內(nèi)猜對(duì),輸出失敗信息。關(guān)鍵代碼:
int count = 5;
while (count) {
// 猜數(shù)字邏輯
count--;
}
if (count == 0) {
printf("你失敗了,正確值是:%d\n", r);
}
7. 總結(jié)
通過(guò)這個(gè)猜數(shù)字游戲項(xiàng)目,我們實(shí)踐了以下重要知識(shí)點(diǎn):
- 隨機(jī)數(shù)生成:掌握了
rand()、srand()和time()的配合使用 - 循環(huán)結(jié)構(gòu):綜合運(yùn)用了
while循環(huán)和do-while循環(huán) - 分支結(jié)構(gòu):使用了
if-else和switch-case進(jìn)行條件判斷 - 模塊化設(shè)計(jì):將不同功能封裝成獨(dú)立的函數(shù),提高代碼可讀性和復(fù)用性
- 程序流程控制:通過(guò)
break跳出循環(huán),通過(guò)continue(可擴(kuò)展)控制循環(huán)節(jié)奏
建議:這個(gè)猜數(shù)小練習(xí)雖然簡(jiǎn)單,但它涵蓋了C語(yǔ)言程序設(shè)計(jì)的核心思想。建議你親手敲一遍代碼,理解每一行代碼的作用,這樣才能在面試中游刃有余。
以上就是一文詳解如何使用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字游戲的詳細(xì)內(nèi)容,更多關(guān)于使用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字游戲的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C++面向?qū)ο蠛诵模侯愰g關(guān)系與繼承實(shí)戰(zhàn)指南
C++面向?qū)ο笾?類間耦合與繼承機(jī)制是核心骨架,涉及依賴、關(guān)聯(lián)、組合、繼承五大關(guān)系,及靜態(tài)成員、友元等,本文給大家介紹C++面向?qū)ο蠛诵模侯愰g關(guān)系與繼承深度解析,感興趣的朋友跟隨小編一起看看吧2026-06-06
直觀理解C語(yǔ)言中指向一位數(shù)組與二維數(shù)組的指針
這篇文章主要介紹了直觀理解C語(yǔ)言中指向一位數(shù)組與二維數(shù)組的指針,數(shù)組指針是C語(yǔ)言入門學(xué)習(xí)過(guò)程中的重點(diǎn)和難點(diǎn),需要的朋友可以參考下2016-05-05
Java C++算法題解leetcode1592重新排列單詞間的空格
這篇文章主要為大家介紹了Java C++算法題解leetcode1592重新排列單詞間的空格示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
C++中構(gòu)造函數(shù)的初始化順序說(shuō)明
C++構(gòu)造函數(shù)調(diào)用遵循基類→成員對(duì)象→派生類順序,虛函數(shù)在構(gòu)造中靜態(tài)綁定,完成后動(dòng)態(tài)綁定,拷貝構(gòu)造在初始化、賦值及容器操作時(shí)調(diào)用,RVO優(yōu)化可能避免拷貝,虛函數(shù)重寫自子類起生效,不追溯基類2025-07-07

