1、內存管理
我們需要知道——變量,其實是內存地址的一個抽像名字罷了。在靜態(tài)編譯的程序中,所有的變量名都會在編譯時被轉成內存地址。機器是不知道我們取的名字的,只知道地址。
內存的使用時程序設計中需要考慮的重要因素之一,這不僅由于系統(tǒng)內存是有限的(尤其在嵌入式系統(tǒng)中),而且內存分配也會直接影響到程序的效率。因此,我們要對C語言中的內存管理,有個系統(tǒng)的了解。
在C語言中,定義了4個內存區(qū)間:代碼區(qū);全局變量和靜態(tài)變量區(qū);局部變量區(qū)即棧區(qū);動態(tài)存儲區(qū),即堆區(qū);具體如下:
1>棧區(qū)(stack)— 由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據結構中的棧。
2>堆區(qū)(heap)— 一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數(shù)據結構中的堆是兩回事,分配方式倒是類似于鏈表。
3>全局區(qū)(靜態(tài)區(qū))(static)—全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的 另一塊區(qū)域。 - 程序結束后由系統(tǒng)釋放。
4>常量區(qū)—常量字符串就是放在這里的。 程序結束后由系統(tǒng)釋放。
5>程序代碼區(qū)—存放函數(shù)體的二進制代碼。我們來看張圖:
圖1
首先我們要知道,源代碼編譯成程序,程序是放在硬盤上的,而非內存里!只有執(zhí)行時才會被調用到內存中!我們來看看程序結構,ELF是是Linux的主要可執(zhí)行文件格式。ELF文件由4部分組成,分別是ELF頭(ELF header)、程序頭表(Program header table)、節(jié)(Section)和節(jié)頭表(Section header table)。具體如下:
1>Program header描述的是一個段在文件中的位置、大小以及它被放進內存后所在的位置和大小。即要加載的信息;
2>Sections保存著object 文件的信息,從連接角度看:包括指令,數(shù)據,符號表,重定位信息等等。在圖中,我們可以看到Sections中包括:
text 文本結 存放指令;
rodata 數(shù)據結 readonly;
data 數(shù)據結 可讀可寫;
3>Section頭表(section header table)包含了描述文件sections的信息。每個section在這個表中有一個入口;每個入口給出了該section的名字,大小,等等信息。相當于 索引!
而程序被加載到內存里面,又是如何分布的呢?我們看看上圖中:
1正文和初始化的數(shù)據和未初始化的數(shù)據就是我們所說的數(shù)據段,正文即代碼段;
2>正文段上面是常量區(qū),常量區(qū)上面是全局變量和靜態(tài)變量區(qū),二者占據的就是初始化的數(shù)據和未初始化的數(shù)據那部分;
3>再上面就是堆,動態(tài)存儲區(qū),這里是上增長;
4>堆上面是棧,存放的是局部變量,就是局部變量所在代碼塊執(zhí)行完畢后,這塊內存會被釋放,這里棧區(qū)是下增長;
5>命令行參數(shù)就是001之類的,環(huán)境變量什么的前面的文章已經講過,有興趣的可以去看看。
我們知道,內存分為動態(tài)內存和靜態(tài)內存,我們先講靜態(tài)內存。
1.1靜態(tài)內存
存儲模型決定了一個變量的內存分配方式和訪問特性,在C語言中主要有三個維度來決定:存儲時期 、作用域 、鏈接。
1、存儲時期存儲時期:變量在內存中的保留時間(生命周期)存儲時期分為兩種情況,關鍵是看變量在程序執(zhí)行過程中會不會被系統(tǒng)自動回收掉。
1) 靜態(tài)存儲時期 Static在程序執(zhí)行過程中一旦分配就不會被自動回收。通常來說,任何不在函數(shù)級別代碼塊內定義的變量。無論是否在代碼塊內,只要采用static關鍵字修飾的變量。
2) 自動存儲時期 Automatic除了靜態(tài)存儲以外的變量都是自動存儲時期的,或者說只要是在代碼塊內定義的非static的變量,系統(tǒng)會肚臍自動非配和釋放內存;
2、作用域作用域:一個變量在定義該變量的自身文件中的可見性(訪問或者引用)在C語言中,一共有3中作用域:1) 代碼塊作用域在代碼塊中定義的變量都具有該代碼的作用域。從這個變量定義地方開始,到這個代碼塊結束,該變量是可見的;
2) 函數(shù)原型作用域出現(xiàn)在函數(shù)原型中的變量,都具有函數(shù)原型作用域,函數(shù)原型作用域從變量定義處一直到原型聲明的末尾。
3) 文件作用域一個在所有函數(shù)之外定義的變量具有文件作用域,具有文件作用域的變量從它的定義處到包含該定義的文件結尾處都是可見的;
3、鏈接鏈接:一個變量在組成程序的所有文件中的可見性(訪問或者引用);C語言中一共有三種不同的鏈接:1) 外部鏈接如果一個變量在組成一個程序的所有文件中的任何位置都可以被訪問,則稱該變量支持外部鏈接;
2) 內部鏈接如果一個變量只可以在定義其自身的文件中的任何位置被訪問,則稱該變量支持內部鏈接。
3) 空鏈接如果一個變量只是被定義其自身的當前代碼塊所私有,不能被程序的其他部分所訪問,則成該變量支持空鏈接
我們來看一個代碼示例:
#include
inta = 0;// 全局初始化區(qū)
char*p1;//全局未初始化區(qū)
intmain()
{
intb;//b在棧區(qū)
chars[] = "abc";//棧
char*p2; //p2在棧區(qū)
char*p3 = "123456";//123456