在學習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代碼經過預處理、編譯、彙編、連結4步後生成一個可執行程式。
在 Linux 下,程式是一個普通的可執行檔案,以下列出一個二進制可執行檔案的基本情況:
通過上圖可以得知,在沒有運作程式前,也就是說程式沒有加載到記憶體前,可執行程式内部已經分好3段資訊,分别為代碼區(text)、資料區(data)和未初始化資料區(bss)3 個部分(有些人直接把data和bss合起來叫做靜态區或全局區)。
(1)代碼區
存放 CPU 執行的機器指令。通常代碼區是可共享的(即另外的執行程式可以調用它),使其可共享的目的是對于頻繁被執行的程式,隻需要在記憶體中有一份代碼即可。代碼區通常是隻讀的,使其隻讀的原因是防止程式意外地修改了它的指令。另外,代碼區還規劃了局部變量的相關資訊。
(2)全局初始化資料區/靜态資料區(data段)
該區包含了在程式中明确被初始化的全局變量、已經初始化的靜态變量(包括全局靜态變量和局部靜态變量)和常量資料(如字元串常量)。
(3)未初始化資料區(又叫 bss 區)
存入的是全局未初始化變量和未初始化靜态變量。未初始化資料區的資料在程式開始執行之前被核心初始化為 0 或者空(NULL)。
程式在加載到記憶體前,代碼區和全局區(data和bss)的大小就是固定的,程式運作期間不能改變。然後,運作可執行程式,系統把程式加載到記憶體,除了根據可執行程式的資訊分出代碼區(text)、資料區(data)和未初始化資料區(bss)之外,還額外增加了棧區、堆區。
(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 }