C語言內存分布


C語言內存分布

典型的C語言程序內存表示分區共有5個部分:

  1. 正文段 Text segment
  2. 已初始化數據段(數據段)Initialized data segment
  3. 未初始化數據段(bss)Uninitialized data segment
  4. 堆 Stack
  5. 棧 Heap

具體分布圖

memory layout

各個分區的作用

  • 正文段
    • CPU執行的機器指令部分
    • 通常可共享
    • 常常是只讀的
  • 已初始化數據段(數據段)
    • 包含程序中需明確賦初始值的變量
    • 保存已經初始化的全局變量
  • 未初始化數據段(BSS)
    • 在程序開始執行之前,內核將此段中的數據初始化為0或空指針
    • 保存未初始化的全局變量(注意:即使是賦值為0也是未初始化!
    • 存儲自動變量(如函數形參)及每次函數調用所需保存的信息
    • 每次函數調用時,存放其返回地址及調用者的環境信息(如某些機器寄存器的值)
    • 為最近被調用的函數分配自動變量和臨時變量的存儲空間
    • 動態存儲分配

初始化?

上面提到,對全局變量來說,如果是賦值為0仍是未初始化。下面給出實際實驗結果:

示例代碼1


#include <stdio.h>

int a;

int main(int argc, char const *argv[])
{
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

示例代碼2


#include <stdio.h>

int a = 0;

int main(int argc, char const *argv[])
{
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,各個存儲區域數值沒有變化。

示例代碼3


#include <stdio.h>

int a = 1;

int main(int argc, char const *argv[])
{
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,對全局變量進行真正的初始化之后,bss少了4個字節,data段多出了4個字節。

關於static的問題

示例代碼4

先看看相對上一例子,多了一個局部變量之后的內存分布。


#include <stdio.h>

int a = 1;

int main(int argc, char const *argv[])
{
    int b;
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,內存分布是沒有變化的,局部變量b會在棧上分配到內存。

示例代碼5

如果把b定義成static呢?


#include <stdio.h>

int a = 1;

int main(int argc, char const *argv[])
{
    static int b;
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,此時bss上多出了8個字節。

示例代碼6

如果給b賦初始值0呢?


#include <stdio.h>

int a = 1;

int main(int argc, char const *argv[])
{
    static int b = 0;
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,跟上一個例子相比沒有變化,說明跟全局變量一樣,static變量賦值為0仍是未初始化。

示例代碼7

如果給b賦初始值1呢?


#include <stdio.h>

int a = 1;

int main(int argc, char const *argv[])
{
    static int b = 1;
    printf("hello\n");
    return 0;
}

編譯后查看內存分布:

可以看到,bss少了4個字節,而data多了4個字節,說明靜態變量和全局變量同理,初始化之后是存在data段中的。

關於static的用處

示例代碼8


#include <stdio.h>

int x = 4;

void incre() {
    static int x = 1;
    x *= x + 1;
    printf("%d\n", x);
}

int main(int argc, char const *argv[])
{
    int i;
    for (i = 1; i < x; i++) {
        incre();
    }
    return 0;
}

運行結果為:

可以看到,函數increx的作用域存在於其局部,但是卻在每次調用函數的時候沿用之前的值!這是因為static定義的變量是靜態變量,有着靜態存儲位置(變量存儲位置固定不動,若在代碼中已經初始化則存在於data段,否則存在於bss段),而不是存在於棧上,因此每次調用函數讀取到的變量的值都是靜態存儲區的值。

static的意義

  1. 全局靜態變量

    • 不會被其它文件所訪問和修改
    • 其它文件中可以使用相同名字的變量,不會發生沖突
  2. 局部靜態變量

    • 可以用作計數器,每次函數調用的時候可以進行計數
  3. 靜態函數

    • 其它文件中可以定義相同名字的函數,不會發生沖突
    • 靜態函數不能被其它文件所用
    • 靜態函數會被分配在一個一直使用的存儲器,直到程序退出,避免了調用函數時進棧出棧,提升運行速度

參考書目

  1. 《Unix環境高級編程》(中文第三版)
  2. 《C primer plus》(中文第五版)

參考博客

C語言存儲空間布局以及static詳解——奔人之旅


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM