關於C 中的大小端 位段(惑位域)和記憶體對齊

2021-08-26 11:15:04 字數 4239 閱讀 7921

聽到好幾個朋友說到去一些公司做面試,總是遇到關於大小端、位段(或者叫位域)和記憶體對齊的考題,然後就不知所措了。雖然我認為很多開發根本就用不到這個,但是我認為很有必要學習理解這些知識點,因為它可以讓你更了解c++的,了解程式在記憶體的運**況,也能加深對計算機系統的理解。

宣告:由於本文的**會受到計算機環境的影響,故在此說明本篇博文中的程式的執行環境。

1、microsoft windows 7 ultimate edition service pack 1 (64bit 6.1.7601)

2、microsoft visual studio 2010 version 10.0.40219.1 sp1rel(ultimate--enu)。

3、microsoft .net framework version 4.0.30319 sp1rel

4、microsoft visual c++ 2010

注:雖然系統是64位的,但是我是使用vc++ 2010預設配置,也即是x86平台。所以下面所有示例和文字表述都是基於32位編譯平台。

1、從靜態儲存區分配:此時的內存在程式編譯的時候已經分配好,並且在程式的整個執行期間都存在。全域性變數,static變數等在此儲存。

2、在棧區分配:在程式的相關**執行時建立,執行結束時被自動釋放。區域性變數在此儲存。棧記憶體分配運算內置於處理器的指令集中,效率

高,但容量有限。

3、在堆區分配:動態分配記憶體。用new/malloc時開闢,delete/free時釋放。變數的生存期由使用者指定,靈活,但會有記憶體洩露等問題。

對於像c++中的char這樣的資料型別,它本身就是占用乙個位元組的大小,不會產生什麼問題。但是當數制型別為int,在32bit的系統中,它需要占用4個位元組(32bit),這個時候就會產生這4個位元組在暫存器中的存放順序的問題。比如int maxheight = 0x12345678,&maxheight = 0x0042ffc4。

具體的該怎麼存放呢?這個時候就需要理解計算機的大小端的原理了。

我們常用的x86結構都是小端模式,而大部分dsp,arm也是小端模式,不過有些arm是可以選擇大小端模式。所以對於上面的maxheight是應該以小端模式來存放,具體情況請看下面兩表。

位址0x0042ffc4

0x0042ffc5

0x0042ffc6

0x0042ffc7數值

0x78

0x56

0x34

0x12

上圖為小端模式位址

0x0042ffc4

0x0042ffc5

0x0042ffc6

0x0042ffc7

數值0x12

0x34

0x56

0x78

上圖為大端模式

通過上面的**,可以看出來大小端的不同,在這裡無法討論那種方式更好,個人覺得似乎大端模式更符合我的習慣。(

注:在這裡我還要說一句,其實在計算機記憶體中並不存在所謂的資料型別,比如char,int等的。這個型別在**中的作用就是讓編譯器知道每次應該從那個位址起始讀取多少位的資料,賦值給相應的變數。)

在前面已經提起過,在計算機中是採用二進位制0和1來表示資料的,每乙個0或者1占用1位(bit)儲存空間,8位組成乙個位元組(byte),為計算機中資料型別的最小單位,如char在32bit系統中占用乙個位元組。但是正如我們知道的,有時候程式中的資料可能並不需要這麼的位元組,比如乙個開關的狀態,只有開和關,用1和0分別替代就可以表示。此時開關的狀態只需要一位儲存空間就可以滿足要求。如果用乙個位元組來儲存,顯然浪費了另外的7位儲存空間。所以在c語言中就有了位段(有的也叫位域,其實是乙個東西)這個概念。具體的語法就是在變數名字後面,加上冒號(:)和指定的儲存空間的位數。具體的定義語法如下:

1:  struct 位段名稱
2:
6:
7:  例項
8:
9:  struct node
10:  node;
其實定義很簡單,上面示例的意義是,定義乙個char變數a,占用2位儲存空間,乙個double變數i,以及乙個占用4位儲存的int變數c。請注意這裡改變了變數本來占用位元組的大小,並不是我們常規定義的乙個int變數占用4個位元組,乙個char變數占用1乙個位元組。但是sizeof(node) = ?呢,在實際的執行環境中執行,得到sizeof(node) = 24;為什麼呢?說起來其實也很簡單,位元組對齊,什麼是位元組對齊,待會下乙個段落會具體講解。先來看乙個面試示例,**如下:

首先來分析*****ar2,因為滿足規則f,在vc下不壓縮,同時要滿足規則a、b、c。所以第二個成員需要最低偏移量為4,第乙個成員後需要填充3-byte。再看第二個*****ar,首先成員a、b滿足規則d,故需要填充在0x00ab9138這個位元組內,具體儲存順序見下圖:

b:3

a:3

7

6

5

4

3

2

1

0

0

0

1

0

0

1

1

0

2

6

而第二個成員和第三個成員滿足規則e,位域之和大於sizeof(char)=1的大小,所以需要乙個偏移量。而第四個成員double=8為了滿足規則b,必須在第三個成員之後填充6-byte,滿足最小偏移量8。第五個成員不需要偏移,故無需填充。而第六個成員和第五個成員滿足規則e,所以需要從新的儲存單元開始儲存,偏移量為int=4的整數倍,然後儲存最後的成員e,中間需要填充3-byte。

由此可以得出總的大小為1 + 1 + 6 + 8 + 4 + 4 = 24,滿足規則c,即是24 mode 8 = 0。

#pragma pack(push) //儲存對齊狀態

#pragma pack(n) /設定對齊模數(選擇n和一般情況下選出來的模數的較小者做對齊模數)

#pragma pack(pop) //恢復對齊狀態

C語言位域和位段

c結構體之位域 位段 有些資訊在儲存時,並不需要占用乙個完整的位元組,而只需佔幾個或乙個二進位制位。例如在存放乙個開關量時,只有0和1 兩種狀態,用一位二進位即可。為了節省儲存空間,並使處理簡便,c語言又提供了一種資料結構,稱為 位域 或 位段 所謂 位域 是把乙個位元組中的二進位劃分為幾個不同的區...

C中的位域與大小端問題

位元組內也是有大小端問題,與位元組中的大小端類似 1 little endian中的位應該這樣排列 01234567 即排在前面的是低位。因此,先分配least significant bits 2 而在big endian中,位應該這樣排列 76543210 即排在前面的是高位。因此,先分配mos...

關於大小端 位域的一些概念

大小端 對於像c 中的char這樣的資料型別,它本身就是占用乙個位元組的大小,不會產生什麼問題。但是當數制型別為int,在32bit的系統中,它需要占用4個位元組 32bit 這個時候就會產生這4個位元組在暫存器中的存放順序的問題。比如int maxheight 0x12345678,maxheig...