結(jié)構(gòu)體對(duì)齊的規(guī)則詳解及C++代碼驗(yàn)證
基本概念
CPU一次能讀取多少個(gè)字節(jié)的數(shù)據(jù)主要是看數(shù)據(jù)總線是多少位的,16位CPU一次能讀取2個(gè)字節(jié),32位CPU一次能讀取4個(gè)字節(jié),64位CPU一次能讀取8個(gè)字節(jié)。并且不能跨內(nèi)存區(qū)間訪問(wèn),這句話的意思可以理解為,如果CPU是32位的話,那么可以將整個(gè)內(nèi)存區(qū)間每4個(gè)字節(jié)分為一塊(BLOCK),每次讀取一個(gè)BLOCK的數(shù)據(jù)。
那么對(duì)于下面這個(gè)結(jié)構(gòu)體:
struct st { char c; int i; };
如果不進(jìn)行對(duì)齊操作,char 的地址范圍0x00000000,int的地址范圍為0x00000001----0x00000004,int分布在兩個(gè)不同的BLOCK上,因此要讀取int需要兩次操作;如果進(jìn)行4字節(jié)對(duì)齊,那么int的地址范圍為0x00000004----0x00000007,處在一個(gè)BLOCK上,因此只需一次讀取操作即可。缺點(diǎn)也顯而易見(jiàn),多占用了3個(gè)字節(jié)。這也是典型的一種空間換時(shí)間的方法吧。
結(jié)構(gòu)體對(duì)齊的規(guī)則
結(jié)構(gòu)體對(duì)齊需要滿足以下三條規(guī)則,其中系統(tǒng)對(duì)齊模數(shù)在64位機(jī)器上默認(rèn)為8字節(jié),32位機(jī)器上默認(rèn)為4字節(jié)。通過(guò)預(yù)處理指令#pargma pack(N)可以修改系統(tǒng)模數(shù)為N個(gè)字節(jié)。
1、以結(jié)構(gòu)體第一個(gè)元素的地址為起始地址,亦即結(jié)構(gòu)體的起始地址。由上可知,第一個(gè)元素的偏移量為0;
2、結(jié)構(gòu)體元素對(duì)齊原則:結(jié)構(gòu)體成員的對(duì)齊模數(shù)為類(lèi)型大小與系統(tǒng)對(duì)齊模數(shù)的較小者;結(jié)構(gòu)體成員的偏移量(填充)為對(duì)齊模數(shù)的整數(shù)倍。
3、結(jié)構(gòu)體大小對(duì)齊原則:結(jié)構(gòu)體的對(duì)齊模數(shù)為結(jié)構(gòu)體最大元素與系統(tǒng)對(duì)齊模數(shù)的較小者;結(jié)構(gòu)體的大?。ㄌ畛洌榻Y(jié)構(gòu)體對(duì)齊模數(shù)的整數(shù)倍。
程序驗(yàn)證
測(cè)試環(huán)境為64位Windows ,VS2019,定義結(jié)構(gòu)體st1,包含3個(gè)元素char,int,double,定義系統(tǒng)對(duì)齊模數(shù)為4個(gè)字節(jié)。
#include <iostream> #pragma pack(4) using Tsize = unsigned long long; using namespace std; struct st1 { char c; int i; double db; Tsize ch_offset() { return Tsize((Tsize)&this->c - (Tsize)this); } Tsize int_offset() { return Tsize((Tsize)&this->i - (Tsize)this); } Tsize double_offset() { return Tsize((Tsize)&this->db - (Tsize)this); } }; int main() { cout << "st1結(jié)構(gòu)體大小" << sizeof(st1) << endl; cout << "char 偏移量=" << st1().ch_offset() << endl; cout << "int偏移量=" << st1().int_offset() << endl; cout << "double偏移量=" << st1().double_offset() << endl; return 0; }
首先我們按照對(duì)齊規(guī)則來(lái)進(jìn)行分析。第一個(gè)元素為char,類(lèi)型大小為1個(gè)字節(jié),對(duì)齊模數(shù)min(1,4)=1,偏移量為0是對(duì)齊模數(shù)的整數(shù)倍,無(wú)需填充(從這里我們可以看到,第一個(gè)字節(jié)偏移量始終為0,是不需要填充的),下一個(gè)元素的偏移從1開(kāi)始;第二個(gè)元素為int,類(lèi)型大小4個(gè)字節(jié),對(duì)齊模數(shù)為4個(gè)字節(jié),不填充時(shí)偏移量為1,不是4的整數(shù)倍,因此這里需要填充3個(gè)字節(jié),使得int的偏移量為4,且下一個(gè)元素偏移從8開(kāi)始;第三個(gè)元素是double,類(lèi)型大小為8個(gè)字節(jié),對(duì)齊模數(shù)為4,偏移量從8開(kāi)始,是4的整數(shù)倍,因此無(wú)需填充,占用8個(gè)字節(jié),因此結(jié)構(gòu)體的大小為16個(gè)字節(jié)。運(yùn)行程序輸出:
將系統(tǒng)對(duì)齊模數(shù)修改為1 【#pargma pack(1)】,這樣的話,任何情況下都無(wú)需填充(不足一個(gè)字節(jié)的類(lèi)型視為一個(gè)字節(jié)),結(jié)構(gòu)體的大小即為結(jié)構(gòu)體元素大小之和。運(yùn)行程序輸出:
將系統(tǒng)對(duì)齊模數(shù)修改為8 【#pargma pack(8)】,這樣的話,任何情況下都無(wú)需填充(不足一個(gè)字節(jié)的類(lèi)型視為一個(gè)字節(jié)),結(jié)構(gòu)體的大小即為結(jié)構(gòu)體元素大小之和。運(yùn)行程序輸出:
Emmm,好像和系統(tǒng)對(duì)齊模數(shù)為4時(shí)沒(méi)什么變化。那我們將int 和 double的順序換一下呢?
#include <iostream> #pragma pack(8) using Tsize = unsigned long long; using namespace std; struct st1 { char c; double db; int i; Tsize ch_offset() { return Tsize((Tsize)&this->c - (Tsize)this); } Tsize int_offset() { return Tsize((Tsize)&this->i - (Tsize)this); } Tsize double_offset() { return Tsize((Tsize)&this->db - (Tsize)this); } }; int main() { cout << "st1結(jié)構(gòu)體大小" << sizeof(st1) << endl; cout << "char 偏移量=" << st1().ch_offset() << endl; cout << "double偏移量=" << st1().double_offset() << endl; cout << "int偏移量=" << st1().int_offset() << endl; return 0; }
順序調(diào)了下,結(jié)構(gòu)體就比原來(lái)大了8個(gè)字節(jié)!!!從中我們可以看出,將結(jié)構(gòu)體元素從小到大排列,可以最大程度節(jié)省空間。
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
OpenCV?直方圖均衡化的實(shí)現(xiàn)原理解析
直方圖均衡化是通過(guò)拉伸像素強(qiáng)度分布范圍來(lái)增強(qiáng)圖像對(duì)比度的一種方法,今天通過(guò)本文給大家介紹OpenCV?直方圖均衡化的實(shí)現(xiàn)原理解析,感興趣的朋友跟隨小編一起看看吧2022-01-01C++運(yùn)行時(shí)類(lèi)型識(shí)別與轉(zhuǎn)換實(shí)現(xiàn)方法
運(yùn)行時(shí)類(lèi)型識(shí)別可能被認(rèn)為是C++中一個(gè)”次要“的特征,當(dāng)程序員在編程過(guò)程中陷入非常困難的境地時(shí),實(shí)用主義將會(huì)幫助他走出困境2022-10-10C++?拷貝構(gòu)造函數(shù)與賦值的區(qū)別
拷貝構(gòu)造函數(shù)和賦值函數(shù)非常容易混淆,本文主要介紹了C++?拷貝構(gòu)造函數(shù)與賦值的區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2024-04-04利用C++單例模式實(shí)現(xiàn)高性能配置管理器
這篇文章主要為大家詳細(xì)介紹了如何利用C++單例模式實(shí)現(xiàn)高性能配置管理器,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-04-04C語(yǔ)言實(shí)現(xiàn)輸出鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn)
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)輸出鏈表中倒數(shù)第k個(gè)節(jié)點(diǎn),主要涉及鏈表的遍歷操作,是數(shù)據(jù)結(jié)構(gòu)中鏈表的常見(jiàn)操作。需要的朋友可以參考下2014-09-09深入分析C語(yǔ)言存儲(chǔ)類(lèi)型與用戶空間內(nèi)部分布
這篇文章主要介紹了C語(yǔ)言存儲(chǔ)類(lèi)型與用戶空間內(nèi)部分布,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-12-12C語(yǔ)言斷言函數(shù)assert()的學(xué)習(xí)筆記
在C語(yǔ)言庫(kù)函數(shù)中提供了一個(gè)輔助調(diào)試程序的小型庫(kù),它是由assert()宏組成,本文就詳細(xì)的介紹了一下如何使用,感興趣的可以了解一下2021-11-11