1. 程式人生 > >淺談C語言中的堆

淺談C語言中的堆

作業系統堆管理器管理:

堆管理器是作業系統的一個模組,堆管理記憶體分配靈活,按需分配。
大塊記憶體:

  堆記憶體管理者總量很大的作業系統記憶體塊,各程序可以按需申請使用,使用完釋放。
程式手動申請&釋放:

  手工意思是需要寫程式碼去申請malloc和釋放free。
髒記憶體:

  堆記憶體也是反覆使用的,而且使用者用完釋放前不會清除,因此也是髒的。
臨時性:

  堆記憶體只在malloc和free之間屬於我這個程序,而可以訪問。在malloc之前和free之後
都不能再訪問,否則會有不可預料的後果。

堆記憶體使用範例

(1)void 是個指標型別,malloc返回的是一個void

型別的指標,實質上malloc返回的是堆管理器分配給我本次申請的那段記憶體空間的首地址(malloc返回的值其實是一個數字,這個數字表示一個記憶體地址)。為什麼要使用void *作為型別?主要原因是malloc幫我們分配記憶體時只是分配了記憶體空間,至於這段空間將來用來儲存什麼型別的元素malloc是不關心的,由我們程式自己來決定。

(2)什麼是void型別。void型別不表示沒有型別,而表示萬能型別。void的意思就是說這個資料的型別當前是不確定的,在需要的時候可以再去指定它的具體型別。void *型別是一個指標型別,這個指標本身佔4個位元組,但是指標指向的型別是不確定的,換句話說這個指標在需要的時候可以被強制轉化成其他任何一種確定型別的指標,也就是說這個指標可以指向任何型別的元素。

(3)malloc的返回值:成功申請空間後返回這個記憶體空間的指標,申請失敗時返回NULL。所以malloc獲取的記憶體指標使用前一定要先檢驗是否為NULL。

(4)malloc申請的記憶體時用完後要free釋放。free(p);會告訴堆管理器這段記憶體我用完了你可以回收了。堆管理器回收了這段記憶體後這段記憶體當前程序就不應該再使用了。因為釋放後堆管理器就可能把這段記憶體再次分配給別的程序,所以你就不能再使用了。

(5)再呼叫free歸還這段記憶體之前,指向這段記憶體的指標p一定不能丟(也就是不能給p另外賦值)。因為p一旦丟失這段malloc來的記憶體就永遠的丟失了(記憶體洩漏),直到當前程式結束時作業系統才會回收這段記憶體。

(6) malloc(0) malloc申請0位元組記憶體本身就是一件無厘頭事情,一般不會碰到這個需要。 如果真的malloc(0)返回的是NULL還是一個有效指標?答案是:實際分配了16Byte的一段記憶體並且返回了這段記憶體的地址。這個答案不是確定的,因為C語言並沒有明確規定malloc(0)時的表現,由各malloc函式庫的實現者來定義。

  malloc(4) gcc中的malloc預設最小是以16B為分配單位的。如果malloc小於16B的大小時都會返回一個16位元組的大小的記憶體。malloc實現時沒有實現任意自己的分配而是允許一些大小的塊記憶體的分配。 malloc(20)去訪問第25、第250、第2500····會怎麼樣 實戰中:120位元組處正確,1200位元組處正確····終於繼續往後訪問總有一個數字處開始段錯誤了。
  

#include <stdio.h>
#include <stdlib.h>


int main(void)
{
    int *p = (int *)malloc(20);
    // 第二步:檢驗分配是否成功
    if (NULL == p)
    {
        printf("malloc error.\n");
        return -1;
    }

    *(p+3) = 12;
    *(p+300000) = 1234;
    printf("*(p+3) = %d.\n", *(p+3));
    printf("*(p+300000) = %d.\n", *(p+300000));        



/*
    int *p1 = (int *)malloc(4);        // p2-p1 = 0x10 = 16Byte
    int *p2 = (int *)malloc(4);

    printf("p1 = %p.\n", p1);        // p2-p1 = 0x10 = 16Byte
    printf("p2 = %p.\n", p2);
*/

/*
    int *p1 = (int *)malloc(0);
    int *p2 = (int *)malloc(0);

    printf("p1 = %p.\n", p1);        // p2-p1 = 0x10 = 16Byte
    printf("p2 = %p.\n", p2);
    */
/*
    // 需要一個1000int型別元素的陣列

    // 第一步:申請和繫結
    int *p = (int *)malloc(1000*sizeof(int));
    // 第二步:檢驗分配是否成功
    if (NULL == p)
    {
        printf("malloc error.\n");
        return -1;
    }

    // 第三步:使用申請到的記憶體
    //p = NULL;
    //p = &a;    // 如果在free之前給p另外賦值,那麼malloc申請的那段記憶體就丟失掉了
                // malloc後p和返回的記憶體相繫結,p是那段記憶體在當前程序的唯一聯絡人
                // 如果p沒有free之前就丟了,那麼這段記憶體就永遠丟了。丟了的概念就是
                // 在作業系統的堆管理器中這段記憶體是當前程序拿著的,但是你也用不了
                // 所以你想申請新的記憶體來替換使用,這就叫程式“吃記憶體”,學名叫記憶體洩漏
    *(p+0) = 1;
    *(p+1) = 2;
    printf("*(p+0) = %d.\n", *(p+0));
    printf("*(p+1) = %d.\n", *(p+1));                

    *(p+222) = 133;
    *(p+223) = 222;                


    // 第四步:釋放
    free(p);
    p = NULL;

    printf("*(p+222) = %d.\n", *(p+222));
    printf("*(p+223) = %d.\n", *(p+223));
*/
    return 0;
}