天天看點

C基礎學習筆記——01-C基礎第10天(記憶體結構)

在學習C基礎總結了筆記,并分享出來。有問題請及時聯系部落客:​​Alliswell_WP​​,轉載請注明出處。

01-C基礎第10天(記憶體結構)

目錄:

1、作用域

2、記憶體布局 1)記憶體分區 2)存儲類型總結 3)存儲類型總結記憶體操作函數  4)堆區記憶體配置設定和釋放

3、記憶體分區代碼分析(在Linux下測試)

1、作用域

類型 作用域 生命周期
auto變量 一對{}内 目前函數
static局部變量 一對{}内 整個程式運作期
extern變量 整個程式 整個程式運作期
static全局變量 目前檔案 整個程式運作期
extern函數 整個程式 整個程式運作期
static函數 目前檔案 整個程式運作期
register變量 一對{}内 目前函數

2、記憶體布局

1)記憶體分區

C基礎學習筆記——01-C基礎第10天(記憶體結構)

C代碼經過預處理、編譯、彙編、連結4步後生成一個可執行程式。

在 Linux 下,程式是一個普通的可執行檔案,以下列出一個二進制可執行檔案的基本情況:

C基礎學習筆記——01-C基礎第10天(記憶體結構)

 通過上圖可以得知,在沒有運作程式前,也就是說程式沒有加載到記憶體前,可執行程式内部已經分好3段資訊,分别為代碼區(text)、資料區(data)和未初始化資料區(bss)3 個部分(有些人直接把data和bss合起來叫做靜态區或全局區)。

(1)代碼區

存放 CPU 執行的機器指令。通常代碼區是可共享的(即另外的執行程式可以調用它),使其可共享的目的是對于頻繁被執行的程式,隻需要在記憶體中有一份代碼即可。代碼區通常是隻讀的,使其隻讀的原因是防止程式意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關資訊。

(2)全局初始化資料區/靜态資料區(data段)

該區包含了在程式中明确被初始化的全局變量、已經初始化的靜态變量(包括全局靜态變量和局部靜态變量)和常量資料(如字元串常量)。

(3)未初始化資料區(又叫 bss 區)

存入的是全局未初始化變量和未初始化靜态變量。未初始化資料區的資料在程式開始執行之前被核心初始化為 0 或者空(NULL)。

程式在加載到記憶體前,代碼區和全局區(data和bss)的大小就是固定的,程式運作期間不能改變。然後,運作可執行程式,系統把程式加載到記憶體,除了根據可執行程式的資訊分出代碼區(text)、資料區(data)和未初始化資料區(bss)之外,還額外增加了棧區、堆區。

C基礎學習筆記——01-C基礎第10天(記憶體結構)
C基礎學習筆記——01-C基礎第10天(記憶體結構)

(1)代碼區(text segment)

加載的是可執行檔案代碼段,所有的可執行代碼都加載到代碼區,這塊記憶體是不可以在運作期間修改的。

(2)未初始化資料區(BSS)

加載的是可執行檔案BSS段,位置可以分開亦可以緊靠資料段,存儲于資料段的資料(全局未初始化,靜态未初始化資料)的生存周期為整個程式運作過程。

(3)全局初始化資料區/靜态資料區(data segment)

加載的是可執行檔案資料段,存儲于資料段(全局初始化,靜态初始化資料,文字常量(隻讀))的資料的生存周期為整個程式運作過程。

(4)棧區(stack)

棧是一種先進後出的記憶體結構,由編譯器自動配置設定釋放,存放函數的參數值、傳回值、局部變量等。在程式運作過程中實時加載和釋放,是以,局部變量的生存周期為申請到釋放該段棧空間。

(5)堆區(heap)

堆是一個大容器,它的容量要遠遠大于棧,但沒有棧那樣先進後出的順序。用于動态記憶體配置設定。堆在記憶體中位于BSS區和棧區之間。一般由程式員配置設定和釋放,若程式員不釋放,程式結束時由作業系統回收。

2)存儲類型總結

類型 作用域 生命周期 存儲位置
auto變量 一對{}内 目前函數 棧區
static局部變量 一對{}内 整個程式運作期 初始化在data段,未初始化在BSS段
extern變量 整個程式 整個程式運作期 初始化在data段,未初始化在BSS段
static全局變量 目前檔案 整個程式運作期 初始化在data段,未初始化在BSS段
extern函數 整個程式 整個程式運作期 代碼區
static函數 目前檔案 整個程式運作期 代碼區
register變量 一對{}内 目前函數 運作時存儲在CPU寄存器
字元串常量 目前檔案 整個程式運作期 data段

3)存儲類型總結記憶體操作函數

memset()

1 #include <string.h>2 void *memset(void *s, int c, size_t n);
3 功能:将s的記憶體區域的前n個位元組以參數c填入
4 參數:
5     s:需要操作記憶體s的首位址
6     c:填充的字元,c雖然參數為int,但必須是unsigned char , 範圍為0~255
7     n:指定需要設定的大小
8 傳回值:s的首位址      

memcpy()

1 #include <string.h>2 void *memcpy(void *dest, const void *src, size_t n);
3 功能:拷貝src所指的記憶體内容的前n個位元組到dest所值的記憶體位址上。
4 參數:
5     dest:目的記憶體首位址
6     src:源記憶體首位址,注意:dest和src所指的記憶體空間不可重疊
7     n:需要拷貝的位元組數
8 傳回值:dest的首位址      

memmove()

memmove()功能用法和memcpy()一樣,差別在于:dest和src所指的記憶體空間重疊時,memmove()仍然能處理,不過執行效率比memcpy()低些。

memcmp()

1 #include <string.h> 2 int memcmp(const void *s1, const void *s2, size_t n);
 3 功能:比較s1和s2所指向記憶體區域的前n個位元組
 4 參數:
 5     s1:記憶體首位址1
 6     s2:記憶體首位址2
 7     n:需比較的前n個位元組
 8 傳回值:
 9     相等:=0
10     大于:>0
11     小于:<0      

 4)堆區記憶體配置設定和釋放

 malloc()

1 #include <stdlib.h>2 void *malloc(size_t size);
3 功能:在記憶體的動态存儲區(堆區)中配置設定一塊長度為size位元組的連續區域,用來存放類型說明符指定的類型。配置設定的記憶體空間内容不确定,一般使用memset初始化。
4 參數:
5     size:需要配置設定記憶體大小(機關:位元組)
6 傳回值:
7 成功:配置設定空間的起始位址
8 失敗:NULL      

free()

1 #include <stdlib.h>2 void free(void *ptr);
3 功能:釋放ptr所指向的一塊記憶體空間,ptr是一個任意類型的指針變量,指向被釋放區域的首位址。對同一記憶體空間多次釋放會出錯。
4 參數:
5 ptr:需要釋放空間的首位址,被釋放區應是由malloc函數所配置設定的區域。
6 傳回值:無      

3、記憶體分區代碼分析(在Linux下測試)

 練習:傳回堆區位址

1 #include <stdio.h> 2 #include <stdlib.h>
 3 
 4 int *fun()
 5 {
 6     int *tmp = NULL;
 7     tmp = (int *)malloc(sizeof(int));
 8     *tmp = 100;
 9     return tmp;//傳回堆區位址,函數調用完畢,不釋放
10 }
11 
12 int main(int argc, char *argv[])
13 {
14     int *p = NULL;
15     p = fun();
16     printf("*p = %d\n", *p);//ok
17 
18     //堆區空間,使用完畢,手動釋放
19     if (p != NULL)
20     {
21         free(p);
22         p = NULL;
23     }
24 
25     return 0;
26 }