1. 結構體字節(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語言里狸剃,結構體所占的內存是連續(xù)的粮宛,但是各個成員之間的地址不一定是連續(xù)的滞时。所以就出現了"字節(jié)對齊"叁幢。
-
字節(jié)對齊默認原則
- 結構體變量的大小,一定是其最大的數據類型的大小的整數倍坪稽,如果某個數據類型大小不夠曼玩,就填充字節(jié)鳞骤。
- 結構體變量的地址,一定和其第一個成員的地址是相同的演训。
練習
struct Box{
int height;
char a[10];
double width;
char type;
};
int main(void) {
struct Box box;
printf("box = %p\n", &box);
printf("box.height = %p\n", &box.height);
printf("box.a = %p\n", box.a);
printf("box.width = %p\n", &box.width);
printf("box.type = %p\n", &box.type);
printf("box = %d\n", sizeof(box));
return 0;
}
2. 內存四區(qū)
執(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í)行結果
&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()----------------
上面結果每次執(zhí)行結果會有所變化样悟,不同計算機結果也會不同拂募。但是會有如下規(guī)律:
- 局部變量、靜態(tài)變量窟她、動態(tài)分配內存陈症、字符串常量與函數分別放在一起,即使在不同的函數中震糖。
- 變量的存放地址大小有如下特點:
字符串常量與代碼 < 靜態(tài)變量 < 動態(tài)分配內存 < 局部變量
- 靜態(tài)變量录肯、動態(tài)分配內存、字符串常量與函數的相鄰變量地址是遞增的吊说。局部變量相鄰變量地址是遞減的论咏。
- 字符串常量與函數是在一起的。
上面說明代碼在內存中是按類型存放在不同的區(qū)里面的颁井。
gcc -S test.c
gcc --save-temp test.c
readelf -S ./a.out
2.1 棧區(qū)(stack)
由編譯器自動分配和釋放厅贪,主要是存放函數參數的值,局部變量的值雅宾。
2.2 堆區(qū)(heap)
由程序員自己申請分配和釋放养涮,需要malloc()
、calloc()
眉抬、realloc()
函數來申請贯吓,用free()
函數來釋放如果不釋放,可能出現指針懸空/野指針蜀变。
函數不能返回指向棧區(qū)的指針悄谐,但是可以返回指向堆區(qū)的指針。
2.3 數據區(qū)(data)
變量標有static
關鍵字库北,保存了靜態(tài)變量爬舰。
1. 初始化的全局變量和初始化的靜態(tài)變量,在一塊區(qū)域贤惯;
2. 未初始化的全局變量和未初始化的靜態(tài)變量洼专,在一塊區(qū)域棒掠,稱作BSS(Block Started by Symbol:以符號開始的塊)孵构;
3. 靜態(tài)變量的生命周期是整個源程序,而且只能被初始化一次烟很,之后的初始化會被忽略颈墅。
(如果不初始化蜡镶,數值數據將被默認初始化為0
, 字符型數據默認初始化為NULL
)。
整個數據區(qū)的數組恤筛,在程序結束后由系統(tǒng)統(tǒng)一銷毀官还。
2.4 代碼區(qū)(code)
用于存放編譯后的可執(zhí)行代碼,二進制碼毒坛,機器碼望伦。
3. 堆和棧的區(qū)別
No. | 比較方面 | 棧 | 堆 |
---|---|---|---|
1 | 管理方式 | 由系統(tǒng)自動管理,以執(zhí)行函數為單位 | 由程序員手動控制 |
2 | 空間大小 | 空間大小編譯時確定(參數+局部變量) | 具有全局性煎殷,總體無大小限制屯伞。 |
3 | 分配方式 | 函數執(zhí)行,系統(tǒng)自動分配豪直;函數結束劣摇,系統(tǒng)立即自動回收。 | 使用new /malloc() 手動分配釋放弓乙;使用delete /free() 手動釋放 |
4 | 優(yōu)點 | 使用方便末融,不需要關心內存申請釋放。 | 可以跨函數使用暇韧。 |
5 | 缺點 | 只能在函數內部使用勾习。 | 容易造成內存泄露。 |
4. 顯示目標文件區(qū)段大邢橇:size
命令
-
各區(qū)段含義
No. 區(qū)段 名稱 含義 1 text
代碼段(code segment/text segment) 存放程序執(zhí)行代碼的內存區(qū)域语卤。該區(qū)域的大小在運行前已確定,且通常屬于只讀酪刀〈舛妫可能包含一些只讀的常數變量,例如字符串常量等骂倘。 2 data
數據段(data segment) 存放程序中已初始化的全局變量的內存區(qū)域眼滤。數據段屬于靜態(tài)內存分配。 3 bss
BSS段(bss segment) 存放程序中未初始化的全局變量的內存區(qū)域历涝。BSS是英文Block Started by Symbol的簡稱诅需。BSS段屬于靜態(tài)內存分配。 dec
與hex
是前面三個區(qū)域的和荧库,dec
是十進制堰塌,hex
是十六進制。 -
沒有顯示的區(qū)段
No. 區(qū)段 含義 1 棧(stack) 存放程序臨時創(chuàng)建的局部變量分衫,也就是函數括弧 {}
中定義的變量(不包括static
聲明的變量)场刑。在函數被調用時,參數也會被壓入發(fā)起調用的進程棧中蚪战,并且待到調用結束后牵现,函數的返回值也會被存放回棧中铐懊。2 堆(heap) 存放程序動態(tài)分配的內存段,大小并不固定瞎疼,可動態(tài)擴張或縮減科乎。當調用 malloc()
等函數分配內存時,堆被擴張贼急;當調用free()
等函數釋放內存時茅茂,堆被縮減。