1. 程式人生 > >C++中結構體的位元組對齊問題

C++中結構體的位元組對齊問題

  前不久,在C++程式中碰到一個有關結構體位元組對齊的問題。

一。問題描述

在程式中,定義了一個結構體,如下:
typedef struct
{
   char name[33];
   int ID;
   int  age;
} PERSON;

聲明瞭一個該結構體的陣列:
PERSON peo[30];

當從結構體中取出ID欄位給一個int型別的區域性變數賦值時,卻出現異常.
比如結構體中的欄位都已經有初始值
peo[0].ID =4;

下面的賦值語句
int tempID = peo[0].ID;
卻不能正確得到數值4,tempID得到的是67108864.
經檢查67108864是4在高位時的數值大小.
賦值時本來應該是取記憶體中的四個Bytes:"04 00 00 00"
可是取值時卻是用"00 00 00 04" 的方式.
在除錯過程中,從peo[0].ID取值是正確的,得到數字4,可程式執行上面賦值語句後:
tempID還是得到的是67108864.

也就是說,在偵錯程式中取值是正確的,彙編後的程式取值卻是不正確的.
程式在開始用了的很長一段時間並沒有出現這種問題,這個問題是最近才發生的.
真是百思不得其解。還有一點是明確的,程式涉及到網路傳輸。

可是如果把結構體中的字元陣列大小由33改為36,一切正常了!

原理上肯定是結構體的位對齊問題,但為什麼以前編譯使用沒出問題,現在編譯才發生呢?
應該怎麼解決呢?

二。尋找問題的原因。

經過CSDN社群各位老大的幫助,並且自己仔細去了解程式中的編譯條件部分,原則上理解了這問題的本質所在。

發現在網路模組中使用到了"#pram pack(1)"這樣的編譯條件,而其它模組則沒有加入這種編譯條件。

而CSDN中其中一個大蝦是這樣解釋的:“對齊方式是給編譯器看的,編譯器根據這個來決定記憶體佈局。一旦編譯成二進位制檔案記憶體佈局就已經確定了,如果兩段程式碼對同一個結構使用的對齊方式不同,那麼就會對記憶體裡的值做出了不同的解釋,賦值的一方認為char[33]佔了36個位元組,從第37個位元組填寫04 00 00 00,可是讀取的一方認為char[33]只有33個位元組,那就從第34個位元組處取四個位元組當作ID。”

這次網友的解釋,我認為指出了問題的本質所在:“如果兩段程式碼對同一個結構使用的對齊方式不同,那麼就會對記憶體裡的值做出了不同的解釋”。我們的程式給結構體初始化部分是按VC編譯器中預設的結構體8位元組對齊,而在網路模組中,由於使用了"#pram pack(1)",結果從結構體中取值時,編譯器認為結構體是按1位元組對齊,最終導致了問題的產生。

三。解決方法

為了解決這個問題,就需要程式中所有的程式碼對同一個結構體都使用同一種對齊即可。

會有兩種解決辦法:
一是把網路模組中的"#pram pack(1)"去掉,結構體都是按VC編譯器中預設的結構體8位元組方式對齊。
二是設定結構體按1位元組方式進行對齊,程式所有模組都按這種對齊方式編譯。

設定在VC的"project"->"setting..."->"c/c++":struct member aligment改成1 Bytes.