1. 程式人生 > >C程式(程序)的記憶體佈局

C程式(程序)的記憶體佈局

C程式(程序)的記憶體佈局

複製程式碼
#include <stdio.h>
const int a = 10; //全域性常量a
int main(void) {
  const int b = 20; //區域性常量b
  int* pa = (int*)&a;
  int* pb = (int*)&b;
  *pa = 30; //可以嗎?能成功賦值嗎?
  *pb = 30; //可以嗎?能成功賦值嗎?
  return 0;
}
複製程式碼

作為程式設計新手的我,有時候寫程式難免會有種迷糊的感覺,雖然寫著程式碼,但總覺得哪裡不自在不通透...像上面的程式碼,我第一次看到的時候根本沒有自信回答出來,我覺著應該有不少程式設計新人和我一樣吧>o<

 

先從(Linux平臺下)虛擬記憶體管理說起,

  寫C程式時,我們經常會列印一個指標地址,說這個指標指向某某記憶體地址.可這些地址是真實實體記憶體地址嗎?不是!這些只是虛擬記憶體地址.

  當一個C程式調入記憶體開始執行後,在記憶體中就會產生一個程序.而在多工作業系統中每個程序都擁有一片屬於自己的記憶體空間(記憶體沙盤),這個沙盤就是虛擬地址空間,在32位下是一個4GB的大小的地址塊,這些虛擬地址通過頁表對映到實體記憶體.

  但系統並不會真的一下分配給每一個程序4GB的實體記憶體空間的對映=  =(不現實啊),這4GB只能是說邏輯地址,它會隨著程序的真實需要自動擴充套件對映到實體記憶體空間,最大到4GB.

  4GB(地址0-0xFFFFFFFF)其中1GB必須保留給系統核心(這是Linux平臺下),也就是說程序自身只能擁有3GB的地址(0-0xC0000000),如圖

我自己畫的=  =:

  程式碼區:程式(函式)程式碼所在,由編譯而得到的二進位制程式碼被載入至此.程式碼區是隻讀的!有執行許可權.程式碼區一般都從0x08048000地址開始(linux下).值得注意的是,字串字面值(如"Hello World")就儲存在這個區.

  資料段BSS段:合稱靜態區(全域性區),用來儲存靜態(全域性)變數.區別是 前者(資料段)儲存的是已初始化的靜態(全域性)變數,可讀寫. 

後者(BSS段)儲存的是未初始化的靜態(全域性)變數,可讀寫.

  堆:自由儲存區.不像全域性變數和區域性變數的生命週期被嚴格定義,堆區的記憶體分配和釋放是由程式設計師所控制的.申請方式:C中是malloc函式,C++中是new識別符號.

  棧:由系統自動分配和釋放.儲存區域性(自動)變數. 一般說的堆疊,其實是指 棧!

  另外,值得注意的是,是由低地址向高地址分配空間;卻是由高地址向低地址分配空間.

下面這段程式碼進一步說明C程式中各資料的記憶體佈局:

複製程式碼
#include <stdio.h>
#include <stdlib.h>
int i1 = 10; //靜態全域性區(data段)
int i2; //靜態全域性區(bss段)
static int i3 = 30; //靜態全域性區(data段)
const int i4 = 40;  //程式碼區!!!
void fun(int i5) //棧區
{
    int i6 = 60; //棧區
    static int i7 = 70; //靜態全域性區(data段)
    const int i8 = 80; //棧區!!!
    char* str1 = "ABCDE"; //程式碼區(字串常量)
    char str2[] = "ABCDE"; //棧區(字元陣列)
    int* pi = malloc(sizeof(int)); //堆區
    printf("&i5=%p\n", &i5);
    printf("&i6=%p\n", &i6);
    printf("&i7=%p\n", &i7);
    printf("&i8=%p\n", &i8);
    printf("str1=%p\n", str1);
    printf("str2=%p\n", str2);
    printf("pi=%p\n", pi);
    free(pi);
}

int main(void)
{
    printf("&i1=%p\n", &i1);
    printf("&i2=%p\n", &i2);
    printf("&i3=%p\n", &i3);
    printf("&i4=%p\n", &i4);
    fun(50);
    return 0;
}
複製程式碼

 

(gcc編譯)程式輸出:

 

至此,從地址大小比較可以看出

1)靜態(static)全域性變數 和 靜態(static)區域性變數 都在 靜態全域性區.

2)全域性常量i4儲存在程式碼區,而區域性常量i8儲存在棧區.

所以最上面的問題是,程式碼區只讀,修改全域性常量會引發執行時段錯誤,而區域性常量是可以成功賦值修改的.

3)字串字面值在程式碼區(所以不可修改),但是字元指標str1在棧區;字元陣列str2在棧區(所以可以修改).

 

最後要說,上述是Linux下的虛擬記憶體佈局順序,Windows下會有所不同.但是對於C程式中資料應該儲存在哪裡都是一致的.

 

本文相關參考: 剖析程式的記憶體佈局 (建議閱讀)