隨機相關的函數
頭文件 stdlib.h
相關函數 :rand 、srand
rand( rand C++ Reference )
函數聲明:int rand( void );
rand函數返回一個位於 0 - RAND_MAX之間的偽隨機整數。其中RAND_MAX在頭文件 stdlib.h 中定義( 一般為int類型可表示的最大正整數 )。
rand函數通過一個特定的隨機數生成算法生成偽隨機數序列,該算法依據一個初始的種子值進行偽隨機數生成。偽隨機數生成可以看做一個特殊的處理過程,對於同一個輸入(種子),輸出的偽隨機值序列總是相同的( 相對於一次程序運行 )。可使用函數 srand 設置偽隨機數算法的種子,rand函數默認的種子值為1。
srand( srand C++ Reference )
函數聲明: void srand( unsigned int seed);
初始化偽隨機序列生成器的種子值。srand函數將偽隨機數生成算法的種子使用參數 seed 初始化,使得偽隨機數生成依賴於不同的種子,避免重復。通過將種子值 seed 設置為不同的可區分的值(如程序運行時的時間),可以保證生成的偽隨機序列較為隨機。
seed = 1時,即為偽隨機序列生成算法的默認初始初始值。
示例:

#include<stdio.h> #include<stdlib.h> #define num_of_loop 8 /*循環次數*/ void generate_random_num(void); /*產生隨機序列並輸出*/ int main(void) { generate_random_num(); /*使用默認的隨機種子,即 1*/ srand( 5 ); /*使用 5 初始化隨機種子*/ generate_random_num(); srand( 1 ); /*使用 1 初始化隨機種子*/ generate_random_num(); return 0; } void generate_random_num(void) { int i; for( i = 0 ; i < num_of_loop ; i++) printf("%d\t",rand()); printf("\n"); }
結果如下圖:
可以看到默認種子的隨機序列和種子值為1的隨機序列是一樣的,而種子值為5的隨機序列則不相同。事實上,對於同一個種子值,每次程序運行產生的隨機序列都是一樣的( 注意不是每次 rand 函數調用,而是每次程序運行 )。即下一次運行上述示例時,仍產生一樣的偽隨機序列。
為了使得由固定種子值產生的固定偽隨機序列是較為隨機的,可以在每次運行程序或調用 rand 函數之前使用不同的種子值來進行初始化。常用的方法是使用在函數調用之時的時間來作為種子值。
時間相關的函數
頭文件:time.h
相關函數:time、clock
time( time C++ Reference )
函數聲明:time_t time( time_t *timer );
time函數的參數可以為空,即 0 / NULL ,此時 time 函數返回一個 time_t 類型的日歷時間;
time函數接受一個指向 time_t 類型變量的指針作為參數,函數返回一個 time_t 類型的日歷時間,並將參數指針指向的變量值設置為該日歷時間。
time函數返回的日歷時間精確到秒級。
根據上文對隨機函數的描述,我們可以使用下面的語句來對隨機數序列的種子初始化,這樣每次進行初始化時的種子在秒級別的精度上是不一樣,故而可以做到較好的隨機。
srand( (int) time(NULL) ) //使用 time 函數返回的日歷時間初始化函數,在秒級進度上保證每次種子值一致
clock( clock C++ Reference )
函數聲明:clock_t clock( void );
clock函數返回程序運行至clock函數處所消耗的處理器時間( 時間片數 ),若調用失敗則返回 -1。
clock函數返回值為運行消耗的時間片數,一個時間片為系統設置的一個常量時間單元,不同的系統時間片長度可能不同。可以通過在程序運行的不同位置設置兩個clock函數,並根據其差值獲得程序運行所消耗的時間片數。在頭文件 time.h 中,存在宏定義 CLOCKS_PER_SEC,定義每秒所包含的時間片數。使用時可以通過 clock 函數得到消耗的時間片數,再通過除法獲得具體消耗的秒數。
其他函數:
gmtime
函數聲明:struct tm* gmtime( const time_t timer );
將time_t 類型的值轉換為UTC時間,並存放在一個 tm 類型的結構體中。
localtime
函數聲明:struct tm* localtime( const time_t timer );
將time_t類型的值轉換為本地時間,並存放在一個 tm 類型的結構體中。
actime
函數聲明:char * actime( const tm *timer);
將 tm 結構體的數據轉換為一個格式固定的字符串。
結構體 tm ( 摘自頭文件time.h )
struct tm { int tm_sec; /* Seconds: 0-59 (K&R says 0-61?) */ int tm_min; /* Minutes: 0-59 */ int tm_hour; /* Hours since midnight: 0-23 */ int tm_mday; /* Day of the month: 1-31 */ int tm_mon; /* Months *since* january: 0-11 */ int tm_year; /* Years since 1900 */ int tm_wday; /* Days since Sunday (0-6) */ int tm_yday; /* Days since Jan. 1: 0-365 */ int tm_isdst; /* +1 Daylight Savings Time, 0 No DST, /* -1 don't know */ };
可以看到,tm結構體記錄了年 月 日 時 分 秒 等信息,可通過 asctime 函數轉換為方便顯示的字符串形式。
示例:

#include<time.h> #include<stdio.h> int main(void) { char *local_time = NULL ; char *UTC_time = NULL ; struct tm *t_in_tm = NULL ; time_t t = time( NULL ); t_in_tm = localtime( &t ); //將time_t類型的數值參數轉換為tm結構體中保存(本地時間) local_time = asctime( t_in_tm ); //將tm類型結構體中的數值轉換為字符串形式 printf("local time is %s\n",local_time); t_in_tm = gmtime( &t ); //將time_t類型的數值參數轉換為tm結構體中保存(UTC時間) UTC_time = asctime( t_in_tm ); //將tm類型結構體中的數值轉換為字符串形式 printf("UTC time is %s\n",UTC_time); return 0; }
可以看到本地時間與UTC時間相差八個小時
注意:可以從示例代碼中看到,在程序運行過程中,並沒有聲明一個具體的 tm 類型的結構體和存放字符串的數組用於存放數據,而只是聲明了用於指向這些結構的指針用於存放返回值。這是由於上述函數的操作是針對一個公用的靜態緩沖區來進行的,返回的參數為指向該緩沖區內具體結構的指針,故而不需要使用者另行聲明。
在頭文件time.h中有如下警告:
也就是說上述靜態緩沖區是由函數共享的,可能被其他函數覆蓋數據,使用需要小心。
實際上,筆者在書寫示例代碼時,首先書寫的是如下程序(只保留核心部分,讀者可以比較一下與上面示例的微小不同),結果輸出的兩個時間總是相同的,而不是相隔8個小時。
1 time_t t = time( NULL ); 2 t_in_tm = localtime( &t ); //將time_t類型的數值參數轉換為tm結構體中保存(本地時間) 3 local_time = asctime( t_in_tm ); //將tm類型結構體中的數值轉換為字符串形式 4 5 t_in_tm = gmtime( &t ); //將time_t類型的數值參數轉換為tm結構體中保存(UTC時間) 6 UTC_time = asctime( t_in_tm ); //將tm類型結構體中的數值轉換為字符串形式 7 8 printf("local time is %s\nUTC time is %s\n",local_time,UTC_time);
原因解釋:在第一次函數調用返回( 2,3行),函數返回的是指向靜態緩沖區中 tm 結構和字符串所在內存的地址。在第二次返回對應結構的內存地址指針時( 5,6行),由於公用靜態緩沖區,后續 gmtime 和 asctime 函數使用的內存空間與 localtime 和 asctime使用的內存空間相同。即第二次操作是在同樣的緩沖區中操作。結果就是,local_time和UTC_time指向的是同一片內存區域,且該區域最后一次是由第6行操作改變的,從而使的兩個字符串輸出的都是UTC時間形式。這也從側面反應出共用靜態緩沖區可能帶來的弊端。
更高精度的時間獲取,可以參考博客 Windows獲取當前系統時間。