函數的可重入與不可重入內部內存分配方式(內存泄漏陷阱,在很多函數中都存在的問題)

如下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指針指向的是同一個指針地址空間。 

  1. #include <iostream>  
  2. #include <ctime>  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     time_t tNow =time(NULL);  
  8.     time_t tEnd = tNow + 3600;  
  9.   
  10.     struct tm* ptm = localtime(&tNow);  
  11.     struct tm* ptmEnd = localtime(&tEnd);  
  12.   
  13.     char szTmp[100] = {0};  
  14.     strftime(szTmp, 100, "%H:%M:%S",ptm);  
  15.   
  16.     char szEnd[100] = {0};  
  17.     strftime(szEnd, 100, "%H:%M:%S",ptmEnd);  
  18.       
  19.     printf("%s\n", szTmp);  
  20.     printf("%s\n", szEnd);  
  21.       
  22.     return 0;  
  23. }  

(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);
}

總結,所以我們在傳入指針時,要傳入一個實體變量的地址,或者一個指向實體變量地址的指針。   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章