《C Primer Plus》讀書筆記——存儲類、鏈接和內存管理

背景

距離上次寫讀書筆記的日子已有半個月了。這段時間一直在做攝像頭直立平衡車,也把《C Primer Plus》的中級部分掃了一遍。現在做賽道算法識別遇到瓶頸了,就想把讀書筆記補回來。原計劃是寫指針和數組2的。現在發現還不如直接開新篇,反正之前沒寫的變長數組之類的也會提及。所以就有這篇存儲類、鏈接和內存管理。雖說這裏部分的知識都有在平時碼代碼時用到,認識得深一點,但是還是有很多過目就忘的,遂爛筆頭記之。

存儲類

C爲變量提供了5種不同的存儲模型,或稱存儲類。還有基於指針的第6種存儲模型。可按照一個變量的存儲時期(storage duration)、作用域(scope)及鏈接(linkage)來描述它。

作用域

作用域描述了程序中可以訪問一個標識符的一個或多個區域。可以是代碼塊作用域、函數原型作用域或者文件作用域。

  • 代碼塊:整個函數體或一個函數內任一複合語句。
    C99允許在一個代碼塊任意位置聲明變量:
    for(int i = 0; i<10; i++) //合法

  • 函數原型:從變量定義處一直到原型聲明的末尾。
    如變長數組:
    void use_a_VLA(int n, int m, ar[n][m]);

  • 文件:在所有函數之外定義的變量,即全局變量。

鏈接

一個C變量具有下列鏈接之一:外部鏈接(external linkage),內部鏈接(internal linkage)或空鏈接(no linkage)。

  • 具有代碼塊作用域或函數原型作用域的變量有空鏈接,意味着他們是由其定義所在代碼塊或函數原型所私有的。
  • 具有外部鏈接:可在多文件使用。
  • 具有內部鏈接:可在單文件使用。

存儲時期

一個C變量有以下兩種存儲時期之一:靜態存儲時期(static storage duration)和自動存儲時期(automatic storage duration)。

  • 具有文件作用域的變量具有靜態存儲時期,它在程序執行期間將一直存在。
    注意:對於具有文件作用域的變量,關鍵詞static表明鏈接類型,並非存儲時期。一個使用static聲明瞭的文件作用域變量具有內部鏈接,而所有的文件作用域變量,無論它具有內部還是外部鏈接,都具有靜態存儲時期。
  • 具有代碼塊作用域的變量一般情況下具有自動存儲時期。
  • 程序進入該代碼塊,將爲這些變量分配內存,退出代碼塊時,將釋放內存。

5種存儲類

存儲類和函數

函數也有存儲類,可以時外部的、靜態的或內聯的。
若無static,默認是extern。

隨機數函數與靜態變量

/* 自寫隨機數函數,包含兩文件 */

/* s_and_r.c */
static unsigned long int next = 1; //種子

int rand(void)
{
    /*產生僞隨機數的公式*/
    next = next * 1103515245 + 12345;
    return (unsigned int)(next / 65536) % 32768;
}

void srand(unsigned int seed)
{
    next = seed;
}


/* r_drive.c */
#include <stdio.h>
extern void srand(unsigned int x);
extern int rand(void);

int main(void)
{
    int count;
    unsigned seed;
    printf("Please enter your choice for seed.\n");
    while(scanf("%u", &seed) == 1)
    {
        srand(seed); //重置種子
        for(count = 0; count < 5; count++)
        printf("%hd\n", rand() );
        printf("Please enter next seed (q to quit):\n"); 
        //實際上輸入的不是無符號十進制數應該就可以退出
    }
    printf("Done.\n");
    return 0;
}

另外還有利用計算機擲出非現實的任意個任意麪骰子的程序。

分配內存:malloc()和free()

函數malloc() 接受一個參數:所需內存字節數。然後malloc()找到可用內存中一個大小適合的塊。內存是匿名的,即malloc()分配了內存,但沒有爲它指定名字。然而,它可以返回那塊內存第一個字節的地址。因此,可以把那個地址賦值給一個指針變量,並使用該指針來訪問那塊內存。malloc()可指向char或void(通用指針)的指針,可返回數組指針、結構指針等等。若找不到所需的空間,它將返回空指針。

double * ptd; //聲明指針來存放塊在內存中的位置
ptd = (double *)malloc(30 * sizeof(double)); //使用malloc請求一個存儲塊,類型指派(double *)在C中可選,在C++必須。

這段代碼請求30個double類型值的空間,並把ptd指向該空間所在位置。注意ptd是作爲指向一個double類型值的指針聲明的。數組的名字是它第一個元素的地址。可用ptd[n]來訪問第n個元素。

現在,創建一個數組有三種方法:

  • 聲明一個數組,聲明時用常量表達式指定數組維數,然後可用數組名訪問數組元素。
  • 聲明一個變長數組,聲明時用變量表達式指定數組維數,然後用數組名來訪問數組元素。
  • 聲明一個指針,調用malloc(),然後使用該指針來訪問數組元素。

一般地,對應每個malloc(),應調用一次free()。free的參數是先前malloc返回的地址,它釋放先前分配的內存。在函數末尾處調用free()可防止內存泄漏(memory leak)。

存儲類與動態內存分配

理想的程序將其可用內存分爲三個獨立的部分:一個是具有外部、內部及空鏈接的靜態變量的;一個是自動變量的,另一個是動態內存分配的。
- 聲明不同存儲類,變量使用內存時間不同,將這一部分內存處理爲一個堆棧。這樣的新變量在內存中創建時按順序加入,消亡時按相反順序移除。
- 動態分配的內存在調用malloc等時產生,調用free時釋放。這樣的內存可能是碎片狀的。

發佈了31 篇原創文章 · 獲贊 91 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章