1. 程式人生 > >c語言動態分配記憶體及記憶體分配部分函式

c語言動態分配記憶體及記憶體分配部分函式

#include<stdio.h>

/**
在C中動態分配記憶體的基本步驟有:
1,用malloc類的函式分配記憶體;
2,用這些記憶體支援應用程式
3,用free函式釋放記憶體
二、動態記憶體分配函式
    malloc :從堆上分配記憶體
    realloc : 在之前分配的記憶體塊的基礎上,將記憶體重新分配為更大或者更小的部分
    calloc: 從堆上分配記憶體並清零
    free:將記憶體塊返回堆
*/
void mainaa()
{
    int *pi = (int*) malloc(sizeof(int));
    *pi = 5;
    printf("*pi:%d\n",*pi);
    free(pi);
}
//為字串分配記憶體,將其初始化,並逐個字元列印字串,然而每次迭代name都會增加1,最後name會指向字串結尾的NUL字元,
//分配記憶體的起始地址丟失了
void mainbb()
{
    //為10個雙精度浮點數分配空間,需要80個位元組
    //double *pd = (double*)malloc(NUMBER_OF_DOUBLES*sizeof(double));
    //以下程式只分配了10個位元組
    const int NUMBER_OF_DOUBLES = 10;
    double *pd = (double*)malloc(NUMBER_OF_DOUBLES);

    //初始化靜態或全域性變數時不能呼叫函式,下面的程式碼宣告一個靜態變數,並試圖用
    //malloc來初始化,這樣會產生一個編譯時錯誤訊息
    //static int *pi = malloc(sizeof(int));

    char *name = (char*)malloc(strlen("Susan")+1);
    strcpy(name,"Susan");

    while(*name != 0){
        printf("%c",*name);
        name++;
    }
}
/**
    使用calloc函式
    calloc會在分配的同時清空記憶體,該函式的原型如下:void *calloc(size_t numElements,size_t elementSize);
    calloc函式會根據numElements和elementSize兩個引數的乘積來分配記憶體,並返回一個指向記憶體的第一個位元組的指標。如果不能分配記憶體
    則返回null,此函式最初用來輔助分配陣列記憶體。
    如果numElements或elementSize為0,那麼calloc可能返回空指標。如果calloc無法分配記憶體就會返回空指標,而且全域性變數errno會設定為ENOMEM(記憶體不足),
    這是POSIX錯誤碼,有的系統上可能沒有
**/
void maincc()
{
    //下面兩端程式碼都是為pi分配了20位元組,全部包含0
    //int *pi = calloc(5,sizeof(int));

    //int *pi = malloc(5*sizeof(int));
    //memset(pi,0,5*sizeof(int));

    /**
        memset函式會用某個值填充記憶體塊,第一個引數是指向要填充的緩衝區的指標,第二個引數是填緩衝區的值,最後一個引數是要填充的位元組數。
        如果記憶體需要清零可以使用calloc,不過執行calloc可能比執行malloc慢。cfree函式已經沒用了。
    */
}
/**
    realloc函式
    realloc函式會重新分配記憶體,原型:void *realloc(void *ptr,size_t size);
    realloc函式返回指向記憶體塊的指標。該函式接受兩個引數,第一個引數是指向原記憶體塊的指標,第二個是請求的大小。重新分配的塊大小和第一個引數
    引用的塊大小不同。返回值是指向重新分配的記憶體的指標。
    請求的大小可以比當前分配的位元組數小或者大。如果比當前分配的小,那麼多餘的記憶體會還給堆,不能保證多餘的記憶體會被清空。如果比當前分配的大,
    那麼可能的話,就在緊挨著當前分配記憶體的區域分配新的記憶體,否則就會在堆的其他區域分配並把舊的記憶體複製到新區域。
    如果大小是0而指標非空,那麼就釋放記憶體。如果無法分配空間,那麼原來的記憶體塊就保持不變,不過返回的指標是空指標,且errno會設定為ENMOEM,
**/
void maindd()
{
    /**
        下例使用兩個變數為字串分配記憶體。一開始分配16個位元組,但只用到了前面的13個位元組(12個十六進位制數字外加null結束字元(0))
    */
    char *string1;
    char *string2;
    string1 = (char*)malloc(16);
    strcpy(string1,"0123456789AB");
    
    /**
        緊接著,用realloc函式指定一個範圍更小的記憶體區域。然後列印這兩個變數的地址和內容
    */
    string2 = realloc(string1,8);
    printf("string1 value:%p [%s]\n",string1,string1);
    printf("string2 value:%p [%s]\n",string2,string2);
}
/**
    alloca函式和變長陣列
    alloca函式(微軟為malloca)在函式的棧幀上分配記憶體。函式返回後會自動釋放記憶體。若低層的執行時系統不基於棧,
    allocal函式會很難實現,所以這個函式時不標準的,如果應用程式需要可移植就儘量避免使用它。
    C99引入了變長陣列(VLA),允許函式內部宣告和建立其長度由變數決定的陣列,比如:
    void compute(int size){
        char * buffer[size];
        ...
    }
    這意味著記憶體分配在執行時完成,且將記憶體作為棧幀的一部分來分配。另外,如果陣列用到sizeof操作符,也是在執行時而不是編譯時執行。
    這麼做只會有一點小小的執行時開銷。而且一旦函式退出,立即釋放記憶體。因為我們沒有用malloc這類函式來建立陣列,所以不應該用free函式來
    釋放它。alloca函式也不應該返回指向陣列所在記憶體的指標,但是可以解決。
    VLA的長度不能改變,一經分配其長度就固定了。
**/

/**
    動態記憶體分配技術
    1,資源獲取即初始化
    資源獲取即初始化(Resource Acquisition Is Initialization,RAII)是Bjarne Stroustrup發明的技術,可以用來解決C++中資源的分配和釋放。
    即使有異常發生,這種技術也能保證資源的初始化和後續的釋放。分配的資源最終總是會得到釋放。
    有好幾種方法可以在C中使用RAII。GNU編譯器提供了非標準的擴充套件來支援這個特性,通過演示如何在一個函式中分配記憶體然後釋放可以說明這種
    擴充套件。一旦變數超出作用域會自動觸發釋放過程。
    GNU的擴充套件需要用到RAII_VARIABLE巨集,它宣告一個變數,然後給變數關聯如下屬性
        1,一個型別。
        2,建立變數時執行的函式。
        3,變數超出作用域時執行的函式。
    這個巨集如下所示:
        #define RAII_VARIABLE(vartype,varname,initval,dtor)\
            void _dtor_ ## varname (vartype * v){dtor(*v);}\
            vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
    在下例中,我們將name變數宣告為字元指標。建立它時會執行malloc函式,為其分配32位元組。當函式結束時,name超出作用域就會執行free函式:
    void raiiExample(){
        RAII_VARIABLE(char*,name,(char*)malloc(32),free);
        strcpy(name,"RAII Example");
        printf("%s\n",name);
    }
    函式執行後會列印"RAII_Example"字串。不用GNU擴充套件也可以達到類似效果。
    2、使用異常處理函式
    另外一種處理記憶體釋放的方法是利用異常處理。儘管異常處理不屬於標準C,但如果可以使用它且不考慮移植問題,它會很有用。下面說明利用
    Microsoft Visua Studio版的C語言的方法。
    這裡的try塊包含任何可能在執行時丟擲異常的語句。不管有沒有異常丟擲,都會執行finally塊,因此也一定會執行free函式。
    void exceptionExample(){
        int *pi = NULL;
        __try{
            pi = (int*)malloc(sizeof(int));
            *pi = 5;
            printf("%d\n",*pi);
        }
        __finally{
            free(pi);
        }
    }
*/