嵌入式C语言之struct内存分配分析
本文源于微信號《嵌入式ARM》。鏈接:http://mp.weixin.qq.com/s/j2mk6jY79nrJge2cDZVH_A
對結(jié)構(gòu)MyStruct采用sizeof會(huì)出現(xiàn)什么結(jié)果呢?sizeof(MyStruct)為多少呢?
?
也許你會(huì)這樣求:sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13
?但是當(dāng)在VC中測試上面結(jié)構(gòu)的大小時(shí),你會(huì)發(fā)現(xiàn)sizeof(MyStruct)=16。你知道為什么在VC中會(huì)得出這樣一個(gè)結(jié)果嗎?
?其實(shí),這是VC對變量存儲(chǔ)的一個(gè)特殊處理。為了提高CPU的存儲(chǔ)速度,VC對一些變量的起始地址做了“對齊”處理。
在默認(rèn)情況下,VC規(guī)定各成員變量存放的起始地址相對于結(jié)構(gòu)的起始地址的偏移量必須為該變量的類型所占用的字節(jié)數(shù)的倍數(shù)。
下面列出常用類型的對齊方式(vs6.0&vs8.0,32位系統(tǒng))。
?
?各成員變量在存放的時(shí)候根據(jù)在結(jié)構(gòu)中出現(xiàn)的順序依次申請空間,同時(shí)按照上面的對齊方式調(diào)整位置,空缺的字節(jié)VC會(huì)自動(dòng)填充。同時(shí)VC為了確保結(jié)構(gòu)的大小為結(jié)構(gòu)的字節(jié)邊界數(shù)(即該結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù))的倍數(shù),所以在為最后一個(gè)成員變量申請空間后,還會(huì)根據(jù)需要自動(dòng)填充空缺的字節(jié)。
?
下面用前面的例子來說明VC到底怎么樣來存放結(jié)構(gòu)的。
?
為上面的結(jié)構(gòu)分配空間的時(shí)候,VC根據(jù)成員變量出現(xiàn)的順序和對齊方式,依次分配。
?(1)先為第一個(gè)成員dda1分配空間,其起始地址跟結(jié)構(gòu)的起始地址相同,剛好偏移量0剛好為sizeof(double)的倍數(shù),該成員變量占用zeof(double)=8個(gè)字節(jié);
?(2)接下來為第二個(gè)成員dda分配空間,這時(shí)下一個(gè)可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為8,是sizeof(char)的倍數(shù),所以把dda存放在偏移量為8的地方滿足對齊方式,該成員變量占用sizeof(char)=1個(gè)字節(jié);
?(3)接下來為第三個(gè)成員type分配空間,這時(shí)下一個(gè)可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為9,不是sizeof(int)=4的倍數(shù),為了滿足對齊方式對偏移量的約束問題,VC自動(dòng)填充3個(gè)字節(jié)(這三個(gè)字節(jié)沒有放什么東西),這時(shí)下一個(gè)可以分配的地址對于結(jié)構(gòu)的起始地址的偏移量為12,剛好是=4的倍數(shù),所以把type存放在偏移量為12的地方,該成員變量占用sizeof(int)=4個(gè)字節(jié);
?(4)這時(shí)整個(gè)結(jié)構(gòu)的成員變量已經(jīng)都分配了空間,總的占用的空間大小為:
8+1+3+4=16,剛好為結(jié)構(gòu)的字節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof(double)=8)的倍數(shù),所以沒有空缺的字節(jié)需要填充。
?(5)所以整個(gè)結(jié)構(gòu)的大小為:
sizeof(MyStruct)=8+1+3+4=16,其中有3個(gè)字節(jié)是VC自動(dòng)填充的,沒有放任何有意義的東西。
?
下面再舉個(gè)例子,交換一下上面的MyStruct的成員變量的位置,使它變成下面的情況:
?
?這個(gè)結(jié)構(gòu)占用的空間為多大呢?在VC6.0環(huán)境下,可以得到sizeof(MyStruct)=24。結(jié)合上面提到的分配空間的一些原則,分析下VC怎么樣為上面的結(jié)構(gòu)分配空間的。
?所有成員變量都分配了空間,空間總的大小為1+7+8+4=20,不是結(jié)構(gòu)的節(jié)邊界數(shù)(即結(jié)構(gòu)中占用最大空間的類型所占用的字節(jié)數(shù)sizeof(double)=8)的倍數(shù),所以需要填充4個(gè)字節(jié),以滿足結(jié)構(gòu)的大小為sizeof(double)=8的倍數(shù)。所以該結(jié)構(gòu)總的大小為:sizeof(MyStruct)為1+7+8+4+4=24。其中總的有7+4=11個(gè)字節(jié)是VC自動(dòng)填充的,沒有放任何有意義的東西。
?
VC對結(jié)構(gòu)的存儲(chǔ)的特殊處理確實(shí)提高CPU存儲(chǔ)變量的速度,但是有時(shí)候也帶來了一些麻煩,我們也屏蔽掉變量默認(rèn)的對齊方式,自己可以設(shè)定變量的對齊方式。VC中提供了#pragma?pack(n)來設(shè)定變量以n字節(jié)對齊方式。n字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況:第一、如果n大于等于該變量所占用的字節(jié)數(shù),那么偏移量必須滿足默認(rèn)的對齊方式,第二、如果n小于該變量的類型所占用的字節(jié)數(shù),那么偏移量為n的倍數(shù),不用滿足默認(rèn)的對齊方式。結(jié)構(gòu)的總大小也有個(gè)約束條件,分下面兩種情況:如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結(jié)構(gòu)的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);否則必須為n的倍數(shù)。
?下面舉例說明其用法:
?
?
以上結(jié)構(gòu)的大小為16,下面分析其存儲(chǔ)情況,首先為m1分配空間,其偏移量為0,滿足我們自己設(shè)定的對齊方式(4字節(jié)對齊),m1占用1個(gè)字節(jié)。接著開始為m4分配空間,這時(shí)其偏移量為1,需要補(bǔ)足3個(gè)字節(jié),這樣使偏移量滿足為n=4的倍數(shù)(因?yàn)?/span>sizeof(double)大于n),m4占用8個(gè)字節(jié)。接著為m3分配空間,這時(shí)其偏移量為12,滿足為4的倍數(shù),m3占用4個(gè)字節(jié)。這時(shí)已經(jīng)為所有成員變量分配了空間,共分配了16個(gè)字節(jié),滿足為n的倍數(shù)。如果把上面的#pragma?pack(4)改為#pragma?pack(16),那么我們可以得到結(jié)構(gòu)的大小為24。
?在VC6中,Project SettingsàC/C++àStruct member alignment中默認(rèn)值為8Bytes *。Struct member alignment用以指定數(shù)據(jù)結(jié)構(gòu)中的成員變量在內(nèi)存中是按幾字節(jié)對齊的,根據(jù)計(jì)算機(jī)數(shù)據(jù)總線的位數(shù),不同的對齊方式存取數(shù)據(jù)的速度不一樣。這個(gè)參數(shù)對數(shù)據(jù)包網(wǎng)絡(luò)傳輸?shù)葢?yīng)用尤為重要,不是存取速度問題,而是數(shù)據(jù)位的精確定義問題,一般在程序中使用#pragma pack來指定。
總結(jié)之,即: (1)可以設(shè)置不同的對齊方式,設(shè)置方式如上面所述,一般系統(tǒng)默認(rèn)為8字節(jié)對齊。而我所知,嵌入式系統(tǒng)中一般默認(rèn)4字節(jié)對齊?(vs2015默認(rèn)4字節(jié)對齊) (2)VC中提供了#pragma?pack(n)來設(shè)定變量以n字節(jié)對齊方式。即在結(jié)構(gòu)體前后分別以#pragma?pack(n),#pragma?pack()進(jìn)行對齊設(shè)置。 n字節(jié)對齊就是說變量存放的起始地址的偏移量有兩種情況: 第一、如果n大于等于該變量類型所占用的字節(jié)數(shù),那么偏移量必須是該類型的整數(shù)倍。 第二、如果n小于該變量的類型所所占用的字節(jié)數(shù),那么偏移量必須是n的整數(shù)倍。
結(jié)構(gòu)的總大小也有個(gè)約束條件,分下面兩種情況: 如果n大于所有成員變量類型所占用的字節(jié)數(shù),那么結(jié)構(gòu)的總大小必須為占用空間最大的變量占用的空間數(shù)的倍數(shù);否則必須為n的倍數(shù)。
總結(jié)
以上是生活随笔為你收集整理的嵌入式C语言之struct内存分配分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符设备驱动基础篇0——驱动开发初体验
- 下一篇: 树——堆的运用