網(wǎng)站首頁 編程語言 正文
前言
本教程可能會用到一點匯編的知識,看不懂沒關(guān)系,知道是那個意思就行了。使用的工具是vs2010。
一、什么是字節(jié)對齊
字節(jié)對齊是字節(jié)按照一定規(guī)則在空間上排列。
現(xiàn)代計算機中內(nèi)存空間都是按照byte劃分的,從理論上講似乎對任何類型的變量的訪問可以從任何地址開始,但實際情況是在訪問特定類型變量的時候經(jīng)常在特 定的內(nèi)存地址訪問,這就需要各種類型數(shù)據(jù)按照一定的規(guī)則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。
在我們之前寫程序的時候可能會發(fā)現(xiàn)有的時候你定義的變量是一個字節(jié),但是他在內(nèi)存中依然按照四個字節(jié)給你存儲,因為在32為系統(tǒng)中,4字節(jié)對齊執(zhí)行效率是最快的。這就是一種犧牲內(nèi)存換取性能的方案。
舉個栗子
我們知道,全局變量在在內(nèi)存中是有一個固定的地址的,如果不重新編譯的話,這個地址是不會改變的。所以我們拿全局變量舉例。
如下,我們定義兩個全局變量:
一個是char(單字節(jié))的一個是int(4字節(jié)的)
然后我們在main函數(shù)中給他們賦值,如下:
然后斷點、F7編譯、F5調(diào)試、ALT+8轉(zhuǎn)到反匯編:
可以看到他們之間差值為4字節(jié)
再次測試,如下:
反匯編:
差值為2。
因為字節(jié)對齊的原因,int類型的起始地址必須是4的整數(shù)倍,short類型的起始地址也必須是2的倍數(shù),當(dāng)然char就沒有那么約束了,因為它只是一個字節(jié)的。并不是說char類型的變量與int變量的差值是因為int類型占了四個字節(jié)所以差值才為4 。
再做測試,如下:
打亂順序。查看反匯編:
總結(jié)如圖。
再次強掉、因為字節(jié)對齊的原因,變量的起始地址必須是變量字節(jié)大小的整數(shù)倍
二、結(jié)構(gòu)體字節(jié)對齊
結(jié)構(gòu)體中成員的存儲方式也是按照上面的字節(jié)對齊方式進行存儲的。不過有一點區(qū)別:結(jié)構(gòu)體的起始地址是其最寬數(shù)據(jù)類型的整數(shù)倍(千萬不要死記:結(jié)構(gòu)體寬度就是最寬數(shù)據(jù)類型的整數(shù)倍,因為這樣容易忘,下面我來和大家分析為什么是這樣,知道為什么才能記得更久)。
舉個栗子
結(jié)果:
我們分析一下:
int 4字節(jié)、char 1字節(jié)、double 8字節(jié),按理說應(yīng)該是13字節(jié)對吧。
分析如下:
int類型占了4個字節(jié),這個時候char類型因為字節(jié)對齊的原因、他的起始地址是1的整數(shù)倍,所以它可以挨著int類型。但是double,他是8個字節(jié)的,由于字節(jié)對齊的原因、他的起始地址必須是8的倍數(shù),所以他和char類型之間會差3。但是int、char、double他們是一個結(jié)構(gòu)體的成員,不是分開存儲的。所以char和double之間空的三個字節(jié)因為字節(jié)對齊的原因必須補齊。
再來:
可以先猜測一下結(jié)果。
結(jié)果如下:
這里大家可以試著推測,只需要記住因為字節(jié)對齊的原因,char起始地址必須是1的整數(shù)倍、int必須是4的整數(shù)倍等等。
三、#pragma pack()的使用
當(dāng)對內(nèi)存要求較高的時候,我們不得不拋棄性能。這個時候可以通過#pragma pack(n)來強制結(jié)構(gòu)體成員的對齊方式
#pragma pack(1)
struct st_info
{
char a;
int b;
};
#pragma pack()
<1> #pragma pack(n)中的n用來設(shè)定變量以n字節(jié)的方式對齊,可以設(shè)定的值包括:1、2、4、8,VC編譯器默認是8。
<2> 若需要強制取消字節(jié)對齊方式,則可使用#pragma pak()取消。
舉個栗子:
示例代碼:
#include <stdio.h>
#include <Windows.h>
#pragma pack(2)
struct stinfo
{
char t;
int x;
char y;
double m;
};
#pragma pack()
int main()
{
printf("%d\n", sizeof(stinfo));
while(1);
return 0;
}
如下:
這里我們讓結(jié)構(gòu)體以2字節(jié)的方式對齊,所以猜想:
char t因為字節(jié)對齊的原因起始地址應(yīng)該是2的整數(shù)倍,但是他是首地址,不用管。
t = 1個字節(jié)。
int x類型因為pack(2)的原因,起始地址應(yīng)該是2的整數(shù)倍,所以它和chart的差值應(yīng)該為2,也就是補1個字節(jié)。
t+1+x = 6個字節(jié)
到這里,t和x已經(jīng)占有6個字節(jié)。
char y因為pack(2)的原因,他的起始地址應(yīng)該是2的整數(shù)倍,但是上面六個字節(jié)正好是2的倍數(shù)。
t+1+x+y = 7個字節(jié)
到這里t、x、y三個成員已經(jīng)占有7個字節(jié)。
double m因為pack(2)的原因,起始地址不得不是2的整數(shù)倍,但是上面7個字節(jié)顯然不對,所以因為字節(jié)對齊的原因,上面t、x、y三個成員需要再補一個字節(jié),這個時候加上double的8個字節(jié)。
t+1+x+y+1+8 = 16個字節(jié)
結(jié)果如下:
猜想正確。
如果我們不強制字節(jié)對齊的話:
char t一個字節(jié)
int x起始地址必須是4的整數(shù)倍,所以t必須補3個字節(jié)
x+t+3 = 8個字節(jié)
char y一個字節(jié)
x+t+3+y = 9個字節(jié)
double m起始地址必須是8的倍數(shù),所以x+t+y需要補7個字節(jié)。
x+t+3+y+7+8 = 24個字節(jié)
結(jié)果如下:
對于pack(1)、pack(4)、pack(8)大家自己嘗試下吧。
總結(jié)
通過上面的講解,想必大家應(yīng)該知道結(jié)構(gòu)體的字節(jié)數(shù)為什么是成員中最寬數(shù)據(jù)類型字節(jié)數(shù)的整數(shù)倍了的,但是還是那句話,不要死記這一點,明白為什么才是重點、才能記得更久。
因為字節(jié)對齊的原因、結(jié)構(gòu)體中成員的起始地址、假設(shè)該成員是int類型,那么它的起始地址必須是4的整數(shù)倍,如果是double類型,那么他的起始地址必須是8的整數(shù)倍。當(dāng)然也可以強制他們的起始地址是一個固定數(shù)字(1、2、4、8)的整數(shù)倍。通過#pragma pack(n)即可。
原文鏈接:https://blog.csdn.net/qq_52572621/article/details/127157452
相關(guān)推薦
- 2023-02-18 Flow轉(zhuǎn)LiveData數(shù)據(jù)丟失原理詳解_Android
- 2022-06-28 python神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)利用PyTorch進行回歸運算_python
- 2024-01-27 解決“該項目不在請確認該項目位置,然后重試” 文件無法刪除問題
- 2024-03-02 前端directus對接單點登錄
- 2022-01-16 npm:使用npm link來調(diào)試本地的包
- 2023-04-07 C#快速實現(xiàn)IList非泛型類接口的自定義類作為數(shù)據(jù)源_C#教程
- 2022-11-14 解決“您的連接不是私密鏈接”的問題
- 2023-07-25 springmvc全局異常處理
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支