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