1. 結(jié)構(gòu)體字節(jié)對齊
struct S1{
char c1;
char c2;
int n;
};
struct S2{
char c1;
int n;
char c2;
};
printf("sizeof(struct S1) = %ld\n",sizeof(struct S1));
printf("sizeof(struct S2) = %ld\n",sizeof(struct S2));
在C語言里述召,結(jié)構(gòu)體所占的內(nèi)存是連續(xù)的,但是各個成員之間的地址不一定是連續(xù)的。所以就出現(xiàn)了"字節(jié)對齊"。
字節(jié)對齊默認原則
結(jié)構(gòu)體變量的大小顾瞻,一定是其最大的數(shù)據(jù)類型的大小的整數(shù)倍幔烛,如果某個數(shù)據(jù)類型大小不夠诚隙,就填充字節(jié)业舍。
結(jié)構(gòu)體變量的地址,一定和其第一個成員的地址是相同的鞍帝。
2.執(zhí)行下面代碼诫睬,分析代碼中各種變量的地址特點。
#include <stdio.h>
#include <stdlib.h>
void f1(){
int f1local1;
int f1local2;
static int f1static1;
static int f1static2;
int* f1dynamic1 = malloc(sizeof(int));
int* f1dynamic2 = malloc(sizeof(int));
printf("--------------f1()----------------\n");
printf("&f1local1 = %p\n",&f1local1);
printf("&f1local2 = %p\n",&f1local2);
printf("&f1static1 = %p\n",&f1static1);
printf("&f1static2 = %p\n",&f1static2);
printf("f1dynamic1 = %p\n",f1dynamic1);
printf("f1dynamic2 = %p\n",f1dynamic2);
printf("\"f1string1\" = %p\n","f1string1");
printf("\"f1string2\" = %p\n","f1string2");
printf("--------------f1()----------------\n");
free(f1dynamic1);
free(f1dynamic2);
}
void f2(){
int f2local1;
int f2local2;
static int f2static1;
static int f2static2;
int* f2dynamic1 = malloc(sizeof(int));
int* f2dynamic2 = malloc(sizeof(int));
printf("--------------f2()----------------\n");
printf("&f2local1 = %p\n",&f2local1);
printf("&f2local2 = %p\n",&f2local2);
printf("&f2static1 = %p\n",&f2static1);
printf("&f2static2 = %p\n",&f2static2);
printf("f2dynamic1 = %p\n",f2dynamic1);
printf("f2dynamic2 = %p\n",f2dynamic2);
printf("\"f2string1\" = %p\n","f2string1");
printf("\"f2string2\" = %p\n","f2string2");
printf("--------------f2()----------------\n");
free(f2dynamic1);
free(f2dynamic2);
}
int static1;
int static2;
int main(){
int local1;
int local2;
int* dynamic1 = malloc(sizeof(int));
int* dynamic2 = malloc(sizeof(int));
printf("&local1 = %p\n",&local1);
printf("&local2 = %p\n",&local2);
printf("&static1 = %p\n",&static1);
printf("&static2 = %p\n",&static2);
printf("dynamic1 = %p\n",dynamic1);
printf("dynamic2 = %p\n",dynamic2);
printf("\"string1\" = %p\n","string1");
printf("\"string2\" = %p\n","string2");
printf("&f1 = %p\n",&f1);
printf("&f2 = %p\n",&f2);
f1();
f2();
}
分析執(zhí)行結(jié)果
&local1 = 0x7ffc8b8120fc
&local2 = 0x7ffc8b8120f8
&static1 = 0x601060
&static2 = 0x601064
dynamic1 = 0x1d10010
dynamic2 = 0x1d10030
"string1" = 0x400b8f
"string2" = 0x400ba7
&f1 = 0x40060d
&f2 = 0x400707
--------------f1()----------------
&f1local1 = 0x7ffc8b8120cc
&f1local2 = 0x7ffc8b8120c8
&f1static1 = 0x601050
&f1static2 = 0x601054
f1dynamic1 = 0x1d10050
f1dynamic2 = 0x1d10070
"f1string1" = 0x400a2f
"f1string2" = 0x400a4b
--------------f1()----------------
--------------f2()----------------
&f2local1 = 0x7ffc8b8120cc
&f2local2 = 0x7ffc8b8120c8
&f2static1 = 0x601058
&f2static2 = 0x60105c
f2dynamic1 = 0x1d10070
f2dynamic2 = 0x1d10050
"f2string1" = 0x400af7
"f2string2" = 0x400b13
--------------f2()----------------
上面結(jié)果每次執(zhí)行結(jié)果會有所變化帕涌,不同計算機結(jié)果也會不同摄凡。但是會有如下規(guī)律:
1.局部變量、靜態(tài)變量蚓曼、動態(tài)分配內(nèi)存亲澡、字符串常量與函數(shù)分別放在一起,即使在不同的函數(shù)中纫版。
變量的存放地址大小有如下特點:
2.字符串常量與代碼 < 靜態(tài)變量 < 動態(tài)分配內(nèi)存 < 局部變量
3.靜態(tài)變量床绪、動態(tài)分配內(nèi)存、字符串常量與函數(shù)的相鄰變量地址是遞增的。局部變量相鄰變量地址是遞減的癞己。
4.字符串常量與函數(shù)是在一起的裹匙。
上面說明代碼在內(nèi)存中是按類型存放在不同的區(qū)里面的。
gcc -S test.c
gcc --save-temp test.c
readelf -S ./a.out
2.1 棧區(qū)(stack)
由編譯器自動分配和釋放末秃,主要是存放函數(shù)參數(shù)的值,局部變量的值籽御。
2.2 堆區(qū)(heap)
由程序員自己申請分配和釋放练慕,需要malloc()、calloc()技掏、realloc()函數(shù)來申請铃将,用free()函數(shù)來釋放如果不釋放,可能出現(xiàn)指針懸空/野指針哑梳。
函數(shù)不能返回指向棧區(qū)的指針劲阎,但是可以返回指向堆區(qū)的指針。
2.3 數(shù)據(jù)區(qū)(data)
變量標有static關(guān)鍵字鸠真,保存了靜態(tài)變量悯仙。
- 初始化的全局變量和初始化的靜態(tài)變量,在一塊區(qū)域吠卷;
- 未初始化的全局變量和未初始化的靜態(tài)變量锡垄,在一塊區(qū)域,稱作BSS(Block Started by Symbol:以符號開始的塊)祭隔;
- 靜態(tài)變量的生命周期是整個源程序货岭,而且只能被初始化一次,之后的初始化會被忽略疾渴。
(如果不初始化千贯,數(shù)值數(shù)據(jù)將被默認初始化為0, 字符型數(shù)據(jù)默認初始化為NULL)。
整個數(shù)據(jù)區(qū)的數(shù)組搞坝,在程序結(jié)束后由系統(tǒng)統(tǒng)一銷毀搔谴。
2.4 代碼區(qū)(code)
用于存放編譯后的可執(zhí)行代碼,二進制碼桩撮,機器碼己沛。
3. 堆和棧的區(qū)別
NO. | 比較方面 | 棧 | 堆 |
---|---|---|---|
1 | 管理方式 | 由系統(tǒng)自動管理,以執(zhí)行函數(shù)為單位 | 由程序員手動控制 |
2 | 空間大小 | 空間大小編譯時確定(參數(shù)+局部變量) | 具有全局性距境,總體無大小限制申尼。 |
3 | 分配方式 | 函數(shù)執(zhí)行,系統(tǒng)自動分配垫桂;函數(shù)結(jié)束师幕,系統(tǒng)立即自動回收。 | 使用new/malloc()手動分配釋放;使用delete/free()手動釋放 |
4 | 優(yōu)點 | 使用方便霹粥,不需要關(guān)心內(nèi)存申請釋放灭将。 | 可以跨函數(shù)使用。 |
5 | 缺點 | 只能在函數(shù)內(nèi)部使用后控。 | 容易造成內(nèi)存泄露庙曙。 |
4. 顯示目標文件區(qū)段大小:size命令
各區(qū)段含義
No. | 區(qū)段 | 名稱 | 含義 |
---|---|---|---|
1 | text | 代碼段(code segment/text segment) | 存放程序執(zhí)行代碼的內(nèi)存區(qū)域浩淘。該區(qū)域的大小在運行前已確定捌朴,且通常屬于只讀≌懦可能包含一些只讀的常數(shù)變量砂蔽,例如字符串常量等。 |
2 | data | 數(shù)據(jù)段(data segment) | 存放程序中已初始化的全局變量的內(nèi)存區(qū)域署惯。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配左驾。 |
3 | bss | BSS段(bss segment) | 存放程序中未初始化的全局變量的內(nèi)存區(qū)域。BSS是英文Block Started by Symbol的簡稱极谊。BSS段屬于靜態(tài)內(nèi)存分配诡右。 |
dec與hex是前面三個區(qū)域的和,dec是十進制轻猖,hex是十六進制稻爬。
No. | 區(qū)段 | 含義 |
---|---|---|
1 | 棧(stack) | 存放程序臨時創(chuàng)建的局部變量,也就是函數(shù)括弧{}中定義的變量(不包括static聲明的變量)蜕依。在函數(shù)被調(diào)用時桅锄,參數(shù)也會被壓入發(fā)起調(diào)用的進程棧中,并且待到調(diào)用結(jié)束后样眠,函數(shù)的返回值也會被存放回棧中友瘤。 |
2 | 堆(heap) | 存放程序動態(tài)分配的內(nèi)存段,大小并不固定檐束,可動態(tài)擴張或縮減辫秧。當調(diào)用malloc()等函數(shù)分配內(nèi)存時,堆被擴張被丧;當調(diào)用free()等函數(shù)釋放內(nèi)存時盟戏,堆被縮減。 |