核心結論
rand() 產生的是偽隨機數列,srand() 設置初始種子決定數列起始位置。同種子 → 同序列,這是確定性算法的本質。
底層實現原理(典型線性同餘法 LCG)
// 簡化版 glibc/MSVC 內部邏輯
static unsigned long next = 1; // 內部狀態
int rand(void) {
next = next * 1103515245 + 12345; // 乘數、增量經調優
return (unsigned int)(next / 65536) % 32768; // 取高 15 位
}
void srand(unsigned int seed) {
next = seed; // 只是重置內部狀態變量
}
為什麼需要種子?
| 場景 | 不調用 srand() | 調用 srand(time(NULL)) |
| 程序每次運行 | 產生完全相同的隨機序列 | 種子不同(時間變化)→ 序列不同 |
| 調試/測試 | 可復現,利於定位 Bug | 不可復現,難以回歸 |
| 遊戲/加密 | 極其危險,攻擊者可預測 | 基礎防禦,仍不夠安全 |
種子的本質
- 只是
rand() 內部狀態變量 next 的初始值
- 類比:隨機數是一本厚書,種子決定「從第幾頁開始讀」
srand() 不產生隨機數,只搬運書籤
正確使用姿勢
#include
#include
int main() {
// 1. 程序啟動時**只調用一次** srand()
srand((unsigned int)time(NULL)); // 以當前秒數為種子
// 2. 之後隨意調用 rand()
for (int i = 0; i < 10; i++)
printf("%d ", rand() % 100); // 0-99
// 3. 需要可復現測試時,固定種子
// srand(12345); // 固定種子,每次序列相同
return 0;
}
常見錯誤避坑
- 循環中重複調用 srand():
// 錯誤!若循環快於 1 秒,time() 返回同值 → 產生同樣隨機數
for (int i=0; i<10; i++) {
srand(time(NULL)); // 災難
printf("%d ", rand());
}
- 種子熵不足:
time(NULL) 只有秒級精度,同秒啟動程序種子相同。高安全場景用 /dev/urandom / CryptGenRandom / getrandom() / std::random_device。
- 以為 rand() 均勻分布:
rand() % N 當 RAND_MAX 不能被 N 整除時有模數偏差。正確做法:拒絕採樣或用浮點縮放。
現代 C/C++ 推薦替代方案
| 語言 | 推薦設施 | 優勢 |
| C11 | <stdalign.h> 無標準隨機庫 | 仍需自建/用第三方 |
| C++11 | <random> : mt19937, uniform_int_distribution | 高質量、類型安全、分布豐富、可移植 |
| C (POSIX) | random()/srandom(), getrandom(), arc4random() | 比 rand() 好,周期長 |
| Linux 內核 | get_random_bytes(), prandom_u32() | 內核級熵源 |
C++11 標準寫法示例
#include
#include
int main() {
std::random_device rd; // 硬件熵源(非確定性)
std::mt19937 gen(rd()); // 梅森旋轉引擎
std::uniform_int_distribution<> dis(1, 100); // 均勻分布 1-100
for (int i = 0; i < 10; i++)
std::cout << dis(gen) << ' ';
return 0;
}
總結:C 語言 rand/srand 只是教學/簡單測試工具,任何正式項目請用 C++ 或平台安全隨機 API。
- 上一篇文章: 初中學歷零基礎自學C語言:可行性分析與具體行動指南
- 下一篇文章: C語言輸入緩衝區殘留換行符導致gets讀取異常的解決方案
Copyright ©
程式碼之家 All Rights Reserved