1. 程式人生 > >C語言基礎及指標⑤動態記憶體分配

C語言基礎及指標⑤動態記憶體分配

接續上篇C語言基礎及指標④函式指標

在上一篇我們瞭解C語言中的函式及函式指標 , 使用函式指標 , 模擬了網路請求的回撥方式 , 今天我們來學習動態記憶體分配。

我們在使用java的時候 , 所有的記憶體都交由JVM做處理 , 我們無法直接控制 , 雖然很少導致記憶體溢位 , 但是程式佔用記憶體卻會越來越大 , 所以我們在使用Android手機的時候 , 剛開始很流暢 , 用著用著就非常卡 , 在開啟大檔案或是播放gif的時候 , 如果採用java編寫處理引擎 , 則會比較卡 , 因為開闢的記憶體空間無法控制 , GC回收又不是即時的 , 這時候就需要我們使用JNI技術 , 使用C語言進行處理 。接下來 ,我們就來學習C語言中的動態記憶體分配 。

C語言中記憶體的大致分配:

記憶體 描述 特性
棧區 是一個確定的常數(win 1~2M) 不同平臺會有不同大小 超出會提示stackoverflow 自動分配 , 自動釋放
堆區 用於動態記憶體分配 手動分配和釋放 , 可佔用80%記憶體
全域性區或靜態區 在程式中明確被初始化的全域性變數、靜態變數(包括全域性靜態變數和區域性靜態變數)和常量資料(如字串常量) 只初始化一次
程式程式碼區 程式碼區指令根據程式設計流程依次執行,對於順序指令,則只會執行一次(每個程序),如果反覆,則需要使用跳轉指令,如果進行遞迴,則需要藉助棧來實現。 程式碼區的指令中包括操作碼和要操作的物件(或物件地址引用)

C語言中動態分配記憶體是在堆區 , java語言中new一個物件 , 也會在堆記憶體中開闢一塊空間 , 來儲存我們建立的這個物件 。在C語言中 , 我們在堆區開闢一塊空間使用的關鍵字是mallocmalloc函式定義:

void* __cdecl malloc(
    _In_ _CRT_GUARDOVERFLOW size_t _Size
    );

使用如下:

// 動態記憶體分配 , 使用malloc函式在對記憶體中開闢連續的記憶體空間 , 單位是:位元組
// 申請一塊40M的堆記憶體
int* p = (int*)malloc(1024 *1024 * 10 * sizeof(int));

下面我們來模擬一下病毒:

/*動態記憶體分配*/
void heapFunc() {
    // 動態記憶體分配 , 使用malloc函式在對記憶體中開闢連續的記憶體空間 , 單位是:位元組
    // 申請一塊40M的堆記憶體
    int* p = (int*)malloc(1024 *1024 * 10 * sizeof(int));
}


void main() {

    while (1)
    {
        // 睡一秒執行一次
        Sleep(1000);
        heapFunc();
    }

    getchar();

}

開啟工作管理員 , 我們可以看到我們共存所佔記憶體 , 正在以40M每秒的速度 , 蹭蹭的往上漲 , 以前的蠕蟲病毒就是如此 , 不斷的消耗記憶體 , 然後導致系統崩潰 。

在使用靜態記憶體分配的時候 , 記憶體大小是固定的 , 很容易超出棧記憶體的最大值, 預估大小往往大大的超出使用大小 , 浪費記憶體 。使用malloc申請記憶體 , 最重要的一個點就是可以動態改變申請的記憶體大小 , 可以使用realloc函式來重新申請記憶體大小,realloc函式定義:

void* __cdecl realloc(
    _Pre_maybenull_ _Post_invalid_ void*  _Block,
    _In_ _CRT_GUARDOVERFLOW        size_t _Size
    );

使用如下:

// 重新申請記憶體大小 , 傳入申請的記憶體指標 , 申請記憶體總大小
int* p2 = realloc(p, (len + add) * sizeof(int));

下面我們來應用一下:

void main() {

    int len;
    printf("請輸入首次分配記憶體大小:");
    scanf("%d", &len);
    // 動態分配記憶體 , 記憶體空間是連續的
    int* p = (int*)malloc(len * sizeof(int));
    // 給申請的記憶體空間賦值
    int i = 0;
    for (; i < len ; i++)
    {    // 生成隨機數賦值
        p[i] = rand() % 100;

        printf("array[%d] = %d , %#x\n", i, p[i], &p[i]);
    }

    // 在原有記憶體上面,重新分配記憶體大小
    printf("請輸入增加的記憶體大小");
    int add;
    scanf("%d", &add);
    // 重新申請記憶體大小 , 傳入申請的記憶體指標 , 申請記憶體總大小
    int* p2 = (int*)realloc(p, (len + add) * sizeof(int));
    // 給新申請的記憶體空間賦值
    int j = len;
    for (; j < len + add ; j++)
    {
        p2[j] = rand() % 200;
    
    }

    // 列印
    j = 0;
    for (; j < len + add; j++)
    {
        printf("array[%d] = %d , %#x\n", j, p2[j], &p2[j]);
    }
    
    // 回收申請的動態記憶體
    if (p2 != NULL)
    {
        free(p2);
        p2 = NULL;
    }
        
    system("pause");
}

使用mallocrealloc配合 , 就可以模擬出我們java中的集合型別,動態改變記憶體空間大小 。 使用malloc 第一次申請的記憶體首地址和第二次申請的記憶體首地址可能相同也可能不同 , 因為申請的記憶體是連續的 , 所有 , 但第一次申請的空間的後續空間不夠用時 , 會重新開闢新的空間 , 並將資料copy到新的空間裡面 。

記憶體分配的幾個注意細節:
1.不能多次釋放
2.釋放完之後 , 給指標置NULL,標誌釋放完成
3.記憶體洩漏 (p重新賦值之後 , 再free , 並沒有真正釋放 , 要在賦值之前釋放前一個記憶體空間)



作者:逝我
連結:https://www.jianshu.com/p/93db7c692d1b
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。