如下3個函數中,localtime爲不可重入,localtime_r可重入,加const爲輸入型參數,不加const爲輸出型參數。
1、struct tm *localtime(const time_t *timep);
2、struct tm *localtime_r(const time_t *timep, struct tm *result);
3、size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
(1)不可重入函數localtime。
這個函數在返回的時候,返回的是一個指針,實際的內存是localtime內部通過static申請的靜態內存,所以通過localtime調用後的返回值不及時使用的話,很有可能被其他線程localtime調用所覆蓋掉,比如CPU執行線程A時使用該函數獲取後,準備打印,這時CPU進入線程B,線程B中正好調用localtime並執行,然後回到線程A中執行printf打印時間,這時線程A的時間被線程B調用的localtime覆蓋了,導致得到錯誤的時間。如下代碼,結果一樣,是因爲ptm與ptmEnd指針指向的是同一個指針地址空間。
- #include <iostream>
- #include <ctime>
- using namespace std;
- int main()
- {
- time_t tNow =time(NULL);
- time_t tEnd = tNow + 3600;
- struct tm* ptm = localtime(&tNow);
- struct tm* ptmEnd = localtime(&tEnd);
- char szTmp[100] = {0};
- strftime(szTmp, 100, "%H:%M:%S",ptm);
- char szEnd[100] = {0};
- strftime(szEnd, 100, "%H:%M:%S",ptmEnd);
- printf("%s\n", szTmp);
- printf("%s\n", szEnd);
- return 0;
- }
(2)可重入函數localtime_r。
可重入函數也是返回一個指針,但是函數內部沒有使用static申請內存,而是需要傳入一個在函數外部申請的內存空間的指針,然後在函數內部運算,將結果放入該外部申請內存空間,最後返回一個指向該內存空間的指針。所以多線程中調用該函數時,外部都申請過內存空間並傳入,然後運算得到當前時間,放入內存,而不是在內部實體static申請。定義太多static容易造成內存泄漏,如果使用localtime獲取一次時間,之後再也不需要獲取,而這個static申請的內存一直存在,並沒有釋放,這就叫內存泄漏。後來查詢了不少資料,發現在localtime_r中有鎖,而多個工作線程同時請求時候,就造成了有些線程等待,會使性能降低。
(3)const 。
函數傳參如果傳的是普通的變量的話,肯定是輸入型的參數。如果傳遞指針的話那麼就有可能是輸入也有可能是輸出型的參數。爲了區別,經常的做法是,如果這個參數是做輸入的,(通常做輸入的在函數內部只需要讀取這個參數,而不會需要更改它)就在指針前面加const來修飾。
(4)定義一個類型變量取地址傳入與定義一個指針傳入
如下代碼:傳入指針本身沒錯,但是該指針並沒有指向一個struct tm類型的實體, 而該函數爲可重入,內部沒有使用static申請內存,只是運算然後放入外部申請的內存,所以這裏要傳入一個實體變量的地址纔行,另一方面,由於沒有const修飾,可知爲輸出型參數,是需要實體變量的。
int main(int argc,char *argv[])
{
struct tm *Time_p = NULL;
time_t tm1;
tm1 = time(NULL);
localtime_r(&tm1,Time_p ); //段錯誤
printf("%d\n",Time_p.tm_sec);
}
如下代碼:不可重入函數內部使用static申請過內存,所以這個指針指向的struct tm類型的地址是有實體的,通過函數返回指向這個實體變量的地址,只是我們看不到這個實體變量。所以不會出現段錯誤。
int main(int argc,char *argv[])
{
struct tm *Time_p = NULL;
time_t tm1;
tm1 = time(NULL);
Time_p = localtime(&tm1);
printf("%d\n",Time_p->tm_sec);
}
總結,所以我們在傳入指針時,要傳入一個實體變量的地址,或者一個指向實體變量地址的指針。