本站電子書

您可以在這些電子書店找到本站電子書: Google Play 圖書iBooks StoreHyReadReadmooPubu
===>>>本站推出學習遊戲意見調查<<<===《如何自學程式設計》入選 Google Play 台灣地區2016年度最佳書籍

C 語言初學教材 - 第五章 回傳指向結構的指標

標準函數庫 time.h 的函數 localtime() 回傳指向結構 tm 的指標,這給了我們一個函數設計的新想法,我們能否直接在處理時間的函數回傳指向結構 my_tm 的指標,使函數間的傳遞不是基本資料型態就是指標,而不用拷貝整個結構浪費記憶體空間呢?



基於這個新想法,我們設計以下的函數原型
My_Time *myTimeS3(time_t seconds);


函數 myTimeS3() 仍是需要 time() 回傳的秒數值當作參數,而函數名稱前的星號 * ,表示 myTimeS3() 回傳的是指標,所以型號前需要註明什麼型態的指標。


定義如下
My_Time *myTimeS3(time_t seconds)
{
    struct tm *tmPtr = localtime(&seconds);
    My_Time mt;
    My_Time *mtPtr = &mt;
    
    mt.year = tmPtr->tm_year + 1900;
    mt.month = tmPtr->tm_mon + 1;
    mt.day = tmPtr->tm_mday;
    mt.hour = tmPtr->tm_hour;
    if (mt.hour < 12 ) {
        mt.isam = 1;
        mt.hourt = tmPtr-<tm_hour;
    }
    else {
        mt.isam = 0;
        mt.hourt = tmPtr-<tm_hour - 12;
    }
    mt.min = tmPtr-<tm_min;
    mt.sec = tmPtr-<tm_sec;

    return mtPtr;
}


嗯,這樣看起來沒錯的,我們依序建立 My_Time 型態的變數 mt ,然後建立指向 mt 的指標變數 mtPtr ,然後將得到的時間轉化為 My_Time 型態,最後回傳指標變數 mtPtr ......


完整的範例程式碼及編譯執行,請繼續參考


咦?為什麼會出現這麼奇怪的結果?


我們不打細究這個奇怪結果,因為我們也無法利用這奇怪的數值。指標變數 mtPtr 所指向的是函數 myTimeS3() 內的結構 mt ,一旦程式執行離開 myTimeS3() ,按理 myTimeS3() 內所有宣告的變數都無法再使用,如果用指標取得記憶體位址讀值,便容易得到垃圾值,也就是記憶體中未定義的殘值。


這樣回傳指標是會發生問題的,因此是個錯誤的設計方式。那如果我們想要如 time.h 的 localtime() 回傳指向 tm 的指標,我們應該怎麼修改呢?其實有幾種方法,我們先來討論利用動態記憶體配置回傳指標。


標準函數庫中 stdlib.h 的函數 malloc() 可以向作業系統要求配置新的記憶體空間,然後回傳該記憶體空間位置的指標,而 malloc() 本身需要新記憶體空間大小的整數值當成參數,通常會用 sizeof 運算子進行計算。sizeof 為關鍵字之一,常用於計算資料型態所需的位元組數。


這樣新函數的原型幾乎沒有多大的變動
My_Time *myTimeS4(time_t seconds);


定義如下
My_Time *myTimeS4(time_t seconds)
{
    struct tm *tmPtr = localtime(&seconds);
    My_Time *mtPtr = malloc(sizeof(My_Time));
    
    mtPtr->year = tmPtr->tm_year + 1900;
    mtPtr->month = tmPtr->tm_mon + 1;
    mtPtr->day = tmPtr->tm_mday;
    mtPtr->hour = tmPtr->tm_hour;
    if (mtPtr->hour < 12 ) {
        mtPtr->isam = 1;
        mtPtr->hourt = tmPtr->tm_hour;
    }
    else {
        mtPtr->isam = 0;
        mtPtr->hourt = tmPtr->tm_hour - 12;
    }
    mtPtr->min = tmPtr->tm_min;
    mtPtr->sec = tmPtr->tm_sec;
    
    return mtPtr;
}


因此 mtPtr 為向作業系統要求配置新記憶體空間的指標,該空間可儲存 My_Time 型態的結構,這個新記憶體空間不屬於函數 myTimeS4() 內宣告的自動變數,因此回傳該指標在 main() 仍可正常運作。


完整的範例程式碼及編譯執行,請繼續參考


問題與討論
  1. 為什麼 myTimeS3() 內宣告的變數都無法在其他地方使用?
  2. 想一想,動態記憶體配置還可以運用在哪些地方?




沒有留言: