歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C程序的構成及動態內存分配

對一個程序,通常的理解就是,源碼編譯成機器代碼,然後通過機器解釋運行。不過是怎樣編譯成機器代碼,和怎樣運行的,無疑是個值得探討的問題。怎樣編譯成機器代碼,過程就是源碼的編譯、鏈接,編譯器做了這些事。而怎樣運行,卻不是哪個器件自己一己之力就可以做到的。機器代碼要在機器上運行,就得要請求硬件資源。涉及最多的就是CPU和內存了。CPU進行邏輯控制和運算,內存用於運行過程中的數據的快速交互場所。

一個C程序從其自身代碼的結構上來看,編譯過後不過是一段代碼。而這段代碼,從磁盤系統加載到內存中被稱為代碼段或正文段(code/text segment)的地方。在內存中的正文段是共享的。所以,當我們運行同一程序的多個進程時,在內存中,只有一個程序代碼的副本。當然,為了防止有人要做壞事,正文段的權限常常都是只讀的。

一個只有謀略,而手下無兵的將領,在戰場上是什麼也做不了的。程序代碼,當然也不可能純粹的都是邏輯描述。還必須得有邏輯作用的對象和結果——數據。也即程序中的各種變量。不同變量,在程序中的地位看起來除了作用域與生存周期外,都大同小異。不過,它們的實現確實非常的不同的。

程序中的未初始化的全局變量,存儲在一個被稱為未初始化數據段的地方。也即是bbs(block started by symbol)段。同時,內核會自動的將此段中的數據初始化為0或空指針。如,函數外的聲明:

int sum[10];

使得該變量存放在bbs段中。

既然有了未初始化的全局變量,當然也就有初始化了的全局變量。它們存放在一個稱為初始化數據段(常簡稱為數據段)的地方。擁有全局的作用域整個程序的生存周期。(Right?) 如,任函數外的聲明:

int tmp = 99;

使得該變量存放在數據段中。

當然,還有一個叫堆棧段的東西。自動變量及每次函數調用是所需要保存的信息都存放在這裡。堆棧的特點是先進後出(FILO)及數據存放的周期短,即進棧出棧操作很頻繁。自動變量通常都是作為臨時變量存在的,存在周期短,非常時候放在棧中。(Right?) 而函數調用時的的現場信息,可以利用堆棧的先進先出特點很方便的進行保護和恢復。典型的,遞歸的實現就是用了堆棧進行,因為堆棧是一層層向下增長的,所以在子函數中是不會覆蓋調用函數的參數的。特別得要注意的一點是,主函數main中的變量,也是自動變量。因為主函數也是函數啊!

最後,作為C和C++特有的動態內存分配。是在運行時,利用堆來動態的進行內存分配的。所以動態分配的內存都具有全局的作用域(從分配後開始)。而生存周期而是直到其被釋放前。動態內存的分配,其實就是向內核請求一塊內存資源,而釋放呢,就是將資源返還給內核。所以動態分配的內存都必須在不再需要時釋放。不然可能會造成內存洩露及動態分配失敗等惱人的問題。

看到這裡,我們可以發現。一個C程序,其代碼是被加載到了內存的代碼段中,而其代碼段中的一個個的變量,並沒有實際的存放數據,數據根據情況的不同存放在了bbs段,數據段,堆棧段及堆中,代碼段中的變量,存放的是一個指向各個實際存放地方的指針。(Right?)

典型的C內存分布圖:

再說說C中動態內存管理。在C中,主要由標准頭文件<stdlib.h>中定義的幾個函數來進行內存管理:

void* malloc(size_t size)

void* calloc(size_t nobj, size_t size)

void* realloc(void *p, size_t size)

void* free(void *p)

上面這四個函數,都返回一個void* 指針。在C中,void* 指針可以接收任意類型指針,同時,可以不經強制類型轉換直接傳遞給任意類型指針。而在C++中,前一種情況一樣,後一種情況必須進行強制類型轉換。

先說說malloc,它接收一個size_t 類型的參數作為請求的內存的大小,以字節為單位。所以,在內存分配中,通常會用到sizeof運算符來獲取要分配的數據類型的大小。如:

int *p = malloc(sizeof(int));

為一個int類型的指針 p 分配了一段內存區域。

calloc函數,用於為一個數組對象分配內存,第一個參數為數組的大小,第二個參數為每中數據類型的大小。一般calloc用得不多,可以直接用malloc替代。

realloc函數,用於調整已分配內存的大小。當你發現你的當前內存太多或太小時,就可以用realloc來進行內存的調整了。

所有這三個內存分配函數失敗時都會返回NULL,所以可以通過檢查返回值來判斷內存分配是否成功。

最後,free函數,用於釋放使用上述三個函數動態分配的內存。並且必須進行釋放。

C中的內存分配函數的實現是由系統來完成的。所以不同系統會有不同的實現。UNIX-like系統中,一般是由sbrk系統調用來實現的。(更具體?)

在C++中,內存管理主要用操作符new 和 delete。相對C來說更方便一些,並且效率更高。不過,有個地方我沒想明白的是,為何STL中的內存管理要單獨寫一個allocator來實現。還是new和delete不夠強大?嗯,畢竟不了解new 和 delete的實現。

Copyright © Linux教程網 All Rights Reserved