「某城市舉辦購物有獎活動,購物額≥200元可參加抽獎,抽到1或9號獲獎」——這是典型的條件分支與用戶交互綜合練習。本文提供標準實現、健壯增強、隨機數進階、擴展重構四個版本,展示從「能跑」到「工程級」的代碼演進。
#include
int main() {
double amount = 0.0;
int lottery_num = 0;
printf("請輸入購物金額:");
if (scanf("%lf", &amount) != 1) {
printf("輸入錯誤:請輸入數字\n");
return 1;
}
if (amount < 200) {
puts("對不起,您不能參加抽獎。");
} else {
printf("輸入10以內的任意一個值 (1-10):");
if (scanf("%d", &lottery_num) != 1) {
printf("輸入錯誤:請輸入整數\n");
return 1;
}
if (lottery_num == 1 || lottery_num == 9) {
puts("恭喜您獲獎!");
} else {
puts("感謝您的參與,謝謝!");
}
}
return 0;
}
#include
#include
// 安全讀取一行並解析double
int read_double(const char *prompt, double *out) {
char buf[256];
while (1) {
printf("%s", prompt);
if (!fgets(buf, sizeof(buf), stdin)) return 0; // EOF
char *endptr;
double val = strtod(buf, &endptr);
if (endptr != buf && (*endptr == '\n' || *endptr == '\0')) {
*out = val;
return 1;
}
printf("輸入無效,請重新輸入數字。\n");
}
}
// 安全讀取整數並校驗範圍
int read_int_range(const char *prompt, int *out, int min, int max) {
char buf[256];
while (1) {
printf("%s", prompt);
if (!fgets(buf, sizeof(buf), stdin)) return 0;
char *endptr;
long val = strtol(buf, &endptr, 10);
if (endptr != buf && (*endptr == '\n' || *endptr == '\0')
&& val >= min && val <= max) {
*out = (int)val;
return 1;
}
printf("輸入無效,請輸入 %d 到 %d 之間的整數。\n", min, max);
}
}
int main() {
double amount;
int lottery_num;
if (!read_double("請輸入購物金額:", &amount)) return 1;
if (amount < 200) {
puts("對不起,您不能參加抽獎。");
return 0;
}
if (!read_int_range("請輸入抽獎號碼 (1-10):", &lottery_num, 1, 10)) return 1;
if (lottery_num == 1 || lottery_num == 9) {
puts("恭喜您獲獎!");
} else {
puts("感謝您的參與,謝謝!");
}
return 0;
}
#include
#include
#include
// 生成 [min, max] 範圍內均勻分布隨機整數
int rand_range(int min, int max) {
// 避免模數偏差:拒絕採樣
int range = max - min + 1;
int limit = RAND_MAX - (RAND_MAX % range);
int r;
do {
r = rand();
} while (r >= limit);
return min + r % range;
}
int main() {
// 僅在程序啟動時播種一次!
srand((unsigned)time(NULL));
double amount;
printf("請輸入購物金額:");
if (scanf("%lf", &amount) != 1) return 1;
if (amount < 200) {
puts("對不起,您不能參加抽獎。");
return 0;
}
puts("正在為您抽獎...");
int winning_num = rand_range(1, 10);
printf("開獎號碼:%d\n", winning_num);
if (winning_num == 1 || winning_num == 9) {
puts("恭喜您獲獎!");
} else {
puts("感謝您的參與,謝謝!");
}
return 0;
}
// lottery.h
#ifndef LOTTERY_H
#define LOTTERY_H
typedef enum { LOTTERY_OK = 0, LOTTERY_ERR_INPUT, LOTTERY_ERR_RANGE } LotteryErr;
typedef struct {
double threshold; // 參與門檻
int *winning_numbers; // 中獎號碼數組
int winning_count; // 中獎號碼個數
int min_num; // 號碼範圍下界
int max_num; // 號碼範圍上界
} LotteryConfig;
typedef struct {
int drawn_number; // 抽中的號碼
int is_winner; // 是否中獎
} LotteryResult;
// 初始化配置 (可從文件/環境變量讀取)
void lottery_init_config(LotteryConfig *cfg);
// 執行抽獎 (分離邏輯與IO,便於單元測試)
LotteryErr lottery_draw(const LotteryConfig *cfg, double amount, int *drawn_num, int *is_winner);
// 交互式運行
void lottery_run_interactive(const LotteryConfig *cfg);
#endif
// lottery.c
#include "lottery.h"
#include
#include
#include
static int seeded = 0;
void lottery_init_config(LotteryConfig *cfg) {
cfg->threshold = 200.0;
static int default_winning[] = {1, 9};
cfg->winning_numbers = default_winning;
cfg->winning_count = 2;
cfg->min_num = 1;
cfg->max_num = 10;
}
static int is_winning_number(const LotteryConfig *cfg, int num) {
for (int i = 0; i < cfg->winning_count; i++) {
if (cfg->winning_numbers[i] == num) return 1;
}
return 0;
}
LotteryErr lottery_draw(const LotteryConfig *cfg, double amount, int *drawn_num, int *is_winner) {
if (amount < cfg->threshold) return LOTTERY_ERR_RANGE;
if (!seeded) {
srand((unsigned)time(NULL));
seeded = 1;
}
int range = cfg->max_num - cfg->min_num + 1;
int limit = RAND_MAX - (RAND_MAX % range);
int r;
do { r = rand(); } while (r >= limit);
*drawn_num = cfg->min_num + r % range;
*is_winner = is_winning_number(cfg, *drawn_num);
return LOTTERY_OK;
}
void lottery_run_interactive(const LotteryConfig *cfg) {
double amount;
printf("請輸入購物金額:");
if (scanf("%lf", &amount) != 1) return;
int drawn, winner;
LotteryErr err = lottery_draw(cfg, amount, &drawn, &winner);
if (err == LOTTERY_ERR_RANGE) {
puts("對不起,您不能參加抽獎。");
return;
}
printf("開獎號碼:%d\n", drawn);
if (winner) puts("恭喜您獲獎!");
else puts("感謝您的參與,謝謝!");
}
// main.c
#include "lottery.h"
int main() {
LotteryConfig cfg;
lottery_init_config(&cfg);
lottery_run_interactive(&cfg);
return 0;
}
// test_lottery.c
#include "lottery.h"
#include
#include
void test_threshold() {
LotteryConfig cfg;
lottery_init_config(&cfg);
int drawn, winner;
// 低於門檻
assert(lottery_draw(&cfg, 199.9, &drawn, &winner) == LOTTERY_ERR_RANGE);
// 等於門檻
assert(lottery_draw(&cfg, 200.0, &drawn, &winner) == LOTTERY_OK);
assert(drawn >= 1 && drawn <= 10);
printf("門檻測試通過\n");
}
void test_winning_numbers() {
LotteryConfig cfg;
lottery_init_config(&cfg);
// 手動設置測試號碼 (需修改lottery_draw支持注入測試號碼)
// 這裡演示邏輯測試思路
assert(is_winning_number(&cfg, 1) == 1);
assert(is_winning_number(&cfg, 9) == 1);
assert(is_winning_number(&cfg, 5) == 0);
printf("中獎號碼邏輯測試通過\n");
}
int main() {
test_threshold();
test_winning_numbers();
printf("所有測試通過!\n");
return 0;
}
| 知識點 | 標準版 | 健壯版 | 進階版 | 工程版 |
|---|---|---|---|---|
| 輸入處理 | scanf直接讀取 | fgets+strtod/strtol循環驗證 | scanf簡化 | 模組化輸入函數 |
| 隨機數 | 用戶輸入模擬 | 用戶輸入模擬 | srand/rand真隨機 | 拒絕採樣避免偏差 |
| 代碼結構 | 單main函數 | 單main+工具函數 | 單main | 分層架構、接口分離 |
| 可測試性 | 無 | 低 | 低 | 高 (邏輯與IO分離) |
| 可配置性 | 硬編碼 | 硬編碼 | 硬編碼 | 配置結構體、外部化 |
從簡單的`if-else`入手,逐步引入「輸入驗證、隨機數、模組化、配置外部化、測試驅動」,這就是C語言程序從「練習題」進化為「產品級代碼」的必經之路。