C语言:--位域和内存对齐
位域
位域是指信息在保存時,并不需要占用一個完整的字節(jié),而只需要占幾個或一個二進制位。為了節(jié)省空間,C語言提供了一種數(shù)據(jù)結(jié)構(gòu),叫“位域”或“位段”。
“位域“是把一個字節(jié)中的二進位劃分為幾個不同的區(qū)域,并說明每個區(qū)域的位數(shù),每個域有一個域名,允許在程序中按位域名進行操作。這樣就可以把幾個不同的對象用一個字節(jié)的二進制位域來表示。
位域的使用和結(jié)構(gòu)成員的使用相同,其一般形式為:位域 變量名.位域名 位域允許用各種格式輸出。
1.?在C中,位域可以寫成這樣(注:位域的數(shù)據(jù)類型一律用無符號的,紀律性)。
struct bitmap {unsigned a : 1;unsigned b : 3;unsigned c : 4; }bit;sizeof(bitmap) == 4;(整個struct的大小為4,因為位域本質(zhì)上是從一個數(shù)據(jù)類型分出來的,在我們的例子中數(shù)據(jù)類型就是unsigned,大小為4,并且位域也是滿足C 的結(jié)構(gòu)體內(nèi)存對齊原則的,等下我們會說到)。
2.?當然了位域也可以有空域。
struct bitmap{unsigned a:4;unsigned :0; /*空域*/unsigned b:4; /*從下一單元開始存放*/unsigned c:4;}sizeof(bitmap) == 8;3.?在這個位域定義中,a占第一字節(jié)的4位,后4位填0表示不使用,b從第二字節(jié)開始,占用4位,c占用4位。這里我們可以看到空域的作用是填充數(shù)據(jù)類型的剩下的位置,有時候我們只是想調(diào)整一下內(nèi)存分配,則我們可以使用無名位域:
struct bitmap {unsigned a:1;unsigned :2;unsigned b:3;unsigned c:2;};sizeof(bitmap) == 4; 4.?如果一個位域的位的分配超過了該類型的位的總數(shù),則從下一個單元開始繼續(xù)分配, 這個很好理解: struct bitmap{unsigned a : 8;unsigned b : 30;unsigned c : 4;};sizeof(bitmap) == 12;注意這個位域的大小是12而不是8,說明如果超了大小是立馬從下一個單元開始分配。
位域的使用主要出現(xiàn)在如下兩種情況:
?(1)當機器可用內(nèi)存空間較少而使用位域可以大量節(jié)省內(nèi)存時。如,當把結(jié)構(gòu)作為大數(shù)組的元素時。
?(2)當需要把一結(jié)構(gòu)或聯(lián)合映射成某預定的組織結(jié)構(gòu)時。例如,當需要訪問字節(jié)內(nèi)的特定位時。
當要把某個成員說明成位域時,其類型只能是int,unsigned int與signed int三者之一(說明:int類型通常代表特定機器中整數(shù)的自然長度。short類型通常為16位,long類型通常為32位,int類型可以為16位或32位.各編譯器可以根據(jù)硬件特性自主選擇合適的類型長度.
關于位域還需要提醒讀者注意如下幾點:
其一,位域的長度不能大于int對象所占用的字位數(shù).例如,若int對象占用16位,則如下位域說明是錯誤的:
???? unsigned int x:17;
其二,由于位域的實現(xiàn)會因編譯程序的不同而不同,在此使用位域會影響程序的可移植性,在不是非要使用位域不可時最好不要使用位域.
其三,盡管使用位域可以節(jié)省內(nèi)存空間,但卻增加了處理時間,在為當訪問各個位域成員時需要把位域從它所在的字中分解出來或反過來把一值壓縮存到位域所在的字位中.
其四,位域的位置不能訪問,因些不能對位域使用地址運算符號&(而對非位域成員則可以使用該運算符).從而,即不能使用指向位域的旨針也不能使用位域的數(shù)組(因為數(shù)組實際上就是一種特殊的指針).另外,位域也不能作為函數(shù)返回的結(jié)果.
最后還要強調(diào)一遍:位域又叫位段(位字段),是一種特殊的結(jié)構(gòu)成員或聯(lián)合成員(即只能用在結(jié)構(gòu)或聯(lián)合中).
內(nèi)存對齊:
1.?說到位域就不得說下內(nèi)存對齊的東西,其實內(nèi)存對齊也很簡單,只是不同的編譯器實現(xiàn)不一樣,至于為什么要內(nèi)存對齊,這個要從CPU的基本工作原理說起,但是首先要明白,無論我們是否內(nèi)存對齊,CPU大多數(shù)情況都是能正常工作的(前提:對于大多數(shù)IA32指令都可以這么說,但是部分指令,如SSE多媒體指令這些就不行,這些指令有特殊內(nèi)存對齊要求,比如16字節(jié)對齊,任何不滿足內(nèi)存對齊的地址訪問儲存器都是會導致異常,對于這些指令,編譯器必須在編譯的時候采取強制內(nèi)存對齊)。
實現(xiàn)內(nèi)存對齊可以提高CPU的性能,比如處理器能一次取出8個字節(jié),這個時候必須要求數(shù)據(jù)地址要8字節(jié)對齊,這個是和CPU和儲存器的外圍電路決定的,在內(nèi)存對齊的情況下,CPU從儲存器取出這8個字節(jié)只需要一個時鐘周期,但是如果這個地址不是8字節(jié)對齊,那么CPU可能就需要兩個時鐘周期才能取出這8個字節(jié)。
對于IA32,每個棧幀都慣例16字節(jié)對齊,編譯器一般也會那么做,但是對于數(shù)據(jù)類型不同的編譯器表現(xiàn)可能不一樣,對于Windows(VC編譯器),任何K字節(jié)的基本對象的地址都必須是K的倍數(shù)(比如對于int,必須4字節(jié)對齊,對于double,必須8字節(jié)對齊),這很大程度上提高了儲存器和CPU的工作性能,但是對存儲空間的浪費比較嚴重;對于Linux,慣例是8字節(jié)數(shù)對齊4字節(jié)邊界(比如double可以4字節(jié)對齊)。對于Windows好Linux,數(shù)據(jù)類型long double都有4字節(jié)對其的要求,對于GCC,long double分配12字節(jié)(雖然它只占10字節(jié)大小)。
所以我們有一般規(guī)則:
struct X {char a; float b; int c; double d; unsigned e; }; sizeof(X) == 32;內(nèi)存對齊狀況應該是下面這個樣子:
struct X {char a; // 1 bytes char padding1[3]; // 3 bytes float b; // 4 bytes int c; // 4 bytes char padding2[4]; // 4 bytes double d; // 8 bytes unsigned e; // 4 bytes char padding3[4]; // 4 bytes}; sizeof(X) == 32;(其中最后的4個字節(jié)的填充是因為規(guī)則4,看下面)。
2.?如果自定義數(shù)據(jù)類型含有位域,則內(nèi)存對齊滿足以下原則:
1. 如果相鄰的位域的數(shù)據(jù)類型相同,則按照分配位的大小來,詳情看我上面寫的位域的第5個情況。
2. 如果相鄰的位域的數(shù)據(jù)類型不相同,則不同編譯器實現(xiàn)不一樣,有些編譯器選擇不壓縮。
3. 如果位域不連續(xù),中間含非位域,則按標準數(shù)據(jù)類型大小劃分,比如:
struct bitmap {unsigned a : 2; int b; unsigned c : 3; }; sizeof(bitmap) == 12;3.?另外可以通過添加#pragma pack(n)來強制改變內(nèi)存分配情況,比如在VC編譯器中:
struct bitmap{unsigned a;double c;};sizeof(bitmap) == 16;加了#pragma pack(4),則強制內(nèi)存對齊4字節(jié),再測試下其大小:
#pragma pack(4) struct bitmap {unsigned a; double c; }; sizeof(bitmap) == 12;當然,如果#pragma pack(n)的n大于本身數(shù)據(jù)類型的寬度,則按數(shù)據(jù)類型的寬度來分配:
struct bitmap {double c; int k; int m; }; sizeof(bitmap) == 16 != 324.?自定義類型(C結(jié)構(gòu)體,C++聚合類)的最后的內(nèi)存對齊,是按照自定義類型內(nèi)的最大類型的寬度來的,比如上面那個例子去掉int m:
struct bitmap{double c;int k;}; sizeof(bitmap) == 16必須以double進行8字節(jié)對齊(VC編譯器)。
聲明:
本文于網(wǎng)絡整理,版權(quán)歸原作者所有,如來源信息有誤或侵犯權(quán)益,請聯(lián)系我們刪除或授權(quán)事宜。
總結(jié)
以上是生活随笔為你收集整理的C语言:--位域和内存对齐的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux怎么运行程序命令(linux怎
- 下一篇: 常见的C语言字符串操作