C語言的內存管理

由於DSP片上資源有限,良好的內存管理有利於提升效率,這裏總結一下關於C的內存管理。

一、一個可執行程序在存儲時(沒有調入到內存)分爲代碼區(text)、數據區(data)和未初始化數據區(bss)3個部分。

1、代碼區(text):存放CPU執行的機器指令(machine instructions),通常,代碼區是可共享的(即另外的執行程序可以調用它),因爲對於頻繁被執行的程序,只需要在內存中有一份代碼即可。代碼區通常是隻讀的,使其只讀的原因是防止程序意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關信息。

2、數據區(data):全局初始化數據區/靜態數據區(initialized data segment/data segment)。該區包含了在程序中明確被初始化的全局變量、靜態變量(包括全局靜態變量和局部靜態變量)和常量數據(如字符串常量)。例如,一個不在任何函數內的聲明(全局數據):int global_A=100;全局變量是不顯式用static修飾的全局變量,但全局變量默認是動態的,作用域是整個工程,在一個文件內定義的全局變量,在另一個文件中,通過extern 全局變量名的聲明,就可以使用全局變量。全局靜態變量是顯式用static修飾的全局變量,作用域是聲明此變量所在的文件,其他的文件即使用extern聲明也不能使用。

3、未初始化數據區(bss)。亦稱BSS區(uninitialized data segment),存入的是全局未初始化變量。例如一個不在任何函數內的聲明:int sum[100]

二、一個正在運行着的C編譯程序佔用的內存分爲代碼區、初始化數據區、未初始化數據區、堆區和棧區5個部分。

(1)代碼區(text segment)。代碼區指令根據程序設計流程依次執行,對於順序指令,則只會執行一次(每個進程),如果反覆,則需要使用跳轉指令,如果進行遞歸,則需要藉助棧來實現。代碼區的指令中包括操作碼和要操作的對象(或對象地址引用)。如果是立即數(即具體的數值,如5),將直接包含在代碼中;如果是局部數據,將在棧區分配空間,然後引用該數據地址;如果是BSS區和數據區,在代碼中同樣將引用該數據地址。

(2)全局初始化數據區/靜態數據區(Data Segment)。只初始化一次。

(3)未初始化數據區(BSS)。在運行時改變其值。

(4)棧區(stack)。由編譯器自動分配釋放,存放函數的參數值、局部變量的值等。其操作方式類似於數據結構中的棧。每當一個函數被調用,該函數返回地址和一些關於調用的信息,比如某些寄存器的內容,被存儲到棧區。然後這個被調用的函數再爲它的自動變量和臨時變量在棧區上分配空間,這就是C實現函數遞歸調用的方法。每執行一次遞歸函數調用,一個新的棧框架就會被使用,這樣這個新實例棧裏的變量就不會和該函數的另一個實例棧裏面的變量混淆。

(5)堆區(heap)。用於動態內存分配。堆在內存中位於bss區和棧區之間。一般由程序員分配和釋放,若程序員不釋放,程序結束時有可能由OS回收。

之所以分成這麼多個區域,主要基於以下考慮:

一個進程在運行過程中,代碼是根據流程依次執行的,只需要訪問一次,當然跳轉和遞歸有可能使代碼執行多次,而數據一般都需要訪問多次,因此單獨開闢空間以方便訪問和節約空間。

臨時數據及需要再次使用的代碼在運行時放入棧區中,生命週期短。

全局數據和靜態數據有可能在整個程序執行過程中都需要訪問,因此單獨存儲管理。

堆區由用戶自由分配,以便管理。

三、棧和堆的區別

棧是由編譯器在需要時分配的,不需要時自動清除的變量存儲區。裏面的變量通常是局部變量、函數參數等。堆是由malloc()函數(C++語言爲new運算符)分配的內存塊,內存釋放由程序員手動控制,在C語言爲free函數完成(C++中爲delete)。棧和堆的主要區別有以下幾點:

(1)管理方式不同。

棧編譯器自動管理,無需程序員手工控制;而堆空間的申請釋放工作由程序員控制,容易產生內存泄漏。

(2)空間大小不同。

棧是向低地址擴展的數據結構,是一塊連續的內存區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,當申請的空間超過棧的剩餘空間時,將提示溢出。因此,用戶能從棧獲得的空間較小。

堆是向高地址擴展的數據結構,是不連續的內存區域。因爲系統是用鏈表來存儲空閒內存地址的,且鏈表的遍歷方向是由低地址向高地址。由此可見,堆獲得的空間較靈活,也較大。棧中元素都是一一對應的,不會存在一個內存塊從棧中間彈出的情況。

(3)是否產生碎片。

對於堆來講,頻繁的malloc/free(new/delete)勢必會造成內存空間的不連續,從而造成大量的碎片,使程序效率降低(雖然程序在退出後操作系統會對內存進行回收管理)。對於棧來講,則不會存在這個問題。

(4)增長方向不同。

堆的增長方向是向上的,即向着內存地址增加的方向;棧的增長方向是向下的,即向着內存地址減小的方向。

(5)分配方式不同。

堆都是程序中由malloc()函數動態申請分配並由free()函數釋放的;棧的分配和釋放是由編譯器完成的,棧的動態分配由alloca()函數完成,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行申請和釋放的,無需手工實現。

(6)分配效率不同。

棧是機器系統提供的數據結構,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行。堆則是C函數庫提供的,它的機制很複雜,例如爲了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考數據結構/操作系統)在堆內存中搜索可用的足夠大的空間,如果沒有足夠大的空間(可能是由於內存碎片太多),就有需要操作系統來重新整理內存空間,這樣就有機會分到足夠大小的內存,然後返回。顯然,堆的效率比棧要低得多。

四、一維數組和二維數組的動態內存分配

一維數組:    

    int length=10;
    int *s=NULL;
    s=(int*)malloc(length*sizeof(int));//分配一個長度爲length的1維數組

    free(s);

二維數組:

        float _Complex **gmask_wave1;
        gmask_wave1=(float _Complex**)malloc(sizeof(float _Complex*)*rows);//爲行分配空間
         for(i=0;i<rows;i++)//爲列分配空間 
        {
            gmask_wave1[i]=(float _Complex*)malloc(sizeof(float _Complex)*cols);
        
        }
        free(gmask_wave1) 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章