1. 程式人生 > >從C語言的記憶體模型研究python的記憶體模型

從C語言的記憶體模型研究python的記憶體模型

C語言的記憶體模型

背景

因為Python底層實際上是由C語言實現的,因此C語言的記憶體模型實際上對python的記憶體模型有很大的影響。

C語言的記憶體分割槽及說明

記憶體分割槽說明
程式程式碼區(code area)存放函式體的二進位制碼
靜態資料區(data area)也稱全域性資料區,包含的資料型別比較多,如全域性變數、靜態變數、一般常量、字串常量。其中:
全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。
常量資料(一般常量、字串常量)存放在另一個區域。
注意:靜態資料區的記憶體在程式結束後由作業系統釋放。
堆區(heap area)一般由程式設計師分配和釋放,若程式設計師不釋放,程式執行結束時由作業系統回收。malloc()calloc()free() 等函式操作的就是這塊記憶體。
注意:這裡所說的堆區與資料結構中的堆不是一個概念,堆區的分配方式倒是類似於連結串列。
棧區(stack area)由系統自動分配釋放,存放函式的引數值、區域性變數的值等。其操作方式類似於資料結構中的棧。
命令列引數區存放命令列引數和環境變數的值,如通過main()函式傳遞的值。
記憶體低地址說明
.text①程式碼區(code section)。由編譯器連結器生成的可執行指令,程式執行時由載入器(loader)從可執行檔案拷貝到記憶體中。為了安全考慮,防止別的區域更改程式碼區資料(即可執行指令),程式碼區具有隻讀屬性。另一個方面,程式碼區通常具有可共享性(sharable),即在記憶體中只有一份程式碼區,如編譯器,假如同時有多個編譯任務在執行,這些編譯任務會共享編譯器的程式碼區,但同時各個編譯任務又有自己獨立的區域。
.rodata②只讀資料區(read-only section)。包含:只讀全域性變數,只讀字串變數,只讀靜態(static)變數。程式執行時由載入器(loader)從可執行檔案拷貝到記憶體中。
.data③可寫資料區(RW section)。包括:可寫全域性變數,可寫靜態(static)變數。程式執行時由載入器(loader)從可執行檔案拷貝到記憶體中。
.bss④未初始化資料區(un-initialized section)。包括:未初始化或初始化為零的全域性變數,未初始化或初始化為零的靜態(static)變數。為了減小可執行檔案的大小,在可執行檔案中bss區只是一個佔位符。在程式執行時,載入器(loader)根據bss區的大小,在記憶體中開闢相應空間,同時將這些記憶體空間全部初始化為零。
heap⑤堆區。對於C語言而言,heap指程式執行時(run-time)由malloc, calloc, realloc等函式分配的記憶體。
可擴充套件區
stack⑥棧區。每一次函式呼叫,都會發生一次壓棧操作,被壓棧資料稱為一個棧幀(stack frame),有多少次函式呼叫(包括main()函式),棧區就有多少個棧幀。相應的,每一次函式呼叫返回,都會相應的發生一次出棧操作,棧幀就會減少一個。
函式呼叫時,根據壓棧的順序,依次需要壓棧的資料包括:呼叫函式(caller funtion)的上下文環境(context environment),如暫存器;函式返回地址;被呼叫函式(called funtion)的引數列表;被呼叫函式的非靜態(static)區域性變數。
當棧區溢位(stack overflow/underflow)時,棧區資料被汙染,程式執行錯誤,甚至“跑飛”(函式返回地址被修改)。
cmd-line arguments and env variables⑦
記憶體高地址

提示:關於區域性的字串常量是存放在全域性的常量區還是棧區,不同的編譯器有不同的實現,VC 將區域性常量像區域性變數一樣對待,儲存於棧(⑥區)中,TC則儲存在靜態資料區的常量區(②區)。

注意:未初始化的全域性變數的預設值是 0,而未初始化的區域性變數的值卻是垃圾值(任意值)。

看一段程式碼:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int a = 0;  // 全域性初始化區(④區)
char *p1;  // 全域性未初始化區(③區)
int main()
{
    int b;  // 棧區
    char s[] = "abc";  // 棧區
    char *p2;  // 棧區
    char *p3 = "123456"; // 123456\0 在常量區(②),p3在棧上,體會與 char s[]="abc"; 的不同
    static int c = 0;  // 全域性初始化區
    p1 = (char *)malloc(10),  // 堆區
    p2 = (char *)malloc(20);  // 堆區
    // 123456\0 放在常量區,但編譯器可能會將它與p3所指向的"123456"優化成一個地方
    strcpy(p1, "123456");
}

本文為讀後的的簡化和轉載,原文地址:C語言記憶體模型