位元組對齊意義
在進行c/c++開發時,特別是要求跨平台或者網路通訊的時候,都會要求進行位元組對齊,那為什麼需要對齊,如果不對齊會有什麼問題呢。
(1) 儲存方式:
現代計算機處理器對儲存的讀取都是按照特定大小位元組去讀寫(稱其為乙個儲存單元),比如乙個變數char,它的長度為1,但是在儲存器中它占用的空間是乙個儲存單元。
當變數位元組長度小於等於乙個儲存單元時,都將分配乙個儲存單元,且儲存空間永遠是儲存單元的整數倍。
(2)讀寫效率:
cpu訪問記憶體都是一次訪問乙個儲存單元,比如32位linux系統乙個儲存單元為4位元組,當需要讀取乙個int資料時,只需要讀取一次,如果儲存單元為1位元組,則需要讀取4次,
通過移位等運算計算出乙個int值,同樣的讀取乙個int值,其訪問記憶體及運算的次數遠遠低於一次讀取乙個位元組。
(3)跨平台問題:
各個硬體平台對儲存空間的處理不盡相同,比如一些cpu訪問特定的變數必須從特定的位址進行讀取,嵌入式arm架構和我們平時使用的x86架構以及不同的作業系統
比如32位作業系統和64位作業系統對儲存的管理和儲存單元的讀寫都不盡相同,如果我們的**需要在不同的平台上執行該怎麼辦,我們不能每種架構和每種系統都開
發一套程式吧。此時我們需要定義一種通用的位元組對齊方式,不管什麼架構與系統都能能資料進行完整正確的讀寫。
(4)網路通訊問題:
在進行網路通訊的時候,如果是相同的平台間進行,則無需關心位元組問題,但是跨平台的通訊,由於不同架構和作業系統對儲存的管理不同,如果不採用傳送及接收方都
滿足的位元組對齊方式,將導致資料的錯亂甚至是程式的崩潰。
(5)位元組對齊解決的問題:
上面所說的問題,其實都是cpu讀寫是以儲存單元進行的,但是我們實際運用的型別的資料大小並非和儲存單元大小相同,所以儲存單元存在位元組填充的問題,我們的
構建的結構體等資料結構,不存在位元組填充,那麼上面的問題都不將存在,所以說,位元組對齊是一種契約,主要解決位元組填充問題。
(6)為什麼選擇8位元組對齊:
就目前而言,64位作業系統預設是8位元組對齊的,8位元組對齊能滿足各種計算架構和系統。
結構體內存布局
比如有如下結構體:
32位作業系統輸出:offset[a: 0, d: 4, c: 12] size: 16
64位作業系統輸出:offset[a: 0, d: 8, c: 16] size: 24
為何差別會這麼大,因為32為作業系統是4位元組對齊(乙個儲存單位為4位元組),64位作業系統位8位元組對齊(乙個儲存單元為8位元組)
如上結構體內存布局如下圖所示:
記憶體布局規則(32位系統4位元組為儲存單元為例):
(1)如果當前的儲存單元空間能不小於當前變數的大小,則儲存到該儲存空間,否則儲存到另乙個儲存空間。
(2)如果變數大小查過儲存單元,則分配多個儲存單元進行儲存。
(3)向後看齊,在乙個儲存單元中存在不同型別,則前面的成員向後補齊,比如存在相鄰兩個成員char和short,在儲存short成員的時候相對char偏移兩個位元組。
8位元組對齊規則
位元組對齊主要是消除系統的位元組填充,規則如下:
(1)結構體內不存在位元組填充。
(2)結構體大小為8位元組的整數倍。
在構建結構體時,注意以下事項:
(1)相同的型別盡量放在一塊
(2)小位元組型別盡量放在大位元組型別之前
(3)避免有填充位元組存在
(4)結構體大小為8的整數倍
8位元組檢測與對齊演算法實現
有時候我們需要一些工具來檢測8位元組是否對齊或者生成乙個對齊的結構體。
因為一般工具都選擇python實現,以下為python實現的8位元組檢測與實現的**(只列出檢測與對齊部分,實際運用中需要進行標頭檔案解析,提取結構體資訊。列舉長度同int,
結構體長度為 8,聯合體當結構體處理,檢測每乙個結構體,如果都是8位元組對齊,那該結構體即為8位元組對齊)
1 #!/usr/bin/python
2 #coding=utf-8
4 bitoffset = lambda x, align: (x+(align-1)) & ~(align-1)5
7 classstview(object):8
9 def __init__(self):10 self.name = none #結構體名
11 self.members = #結構體成員,stmemberview物件
14 classstmemberview(object):15
16 def __init__(self, name=none, type_=none, size=none, number=1):17 self.name = name #結構體成員名
18 self.type = type_ #結構體成員型別
19 self.size = size #結構體成員大小
20 self.number = number #結構體成員陣列大小
23 def check8bit(stview=stview()):24 """
25 8位元組檢測26 :param stview:27 :return:28 """
29 offset =030 for mem instview.members:31 offset2 =bitoffset(offset, int(mem.size))32 if offset2 !=offset:33 print "structural is not 8 bit aligned with . need bit before ".\34 format(stview.name, mem.name, offset2-offset)35 returnfalse36 else:37 offset += mem.number *int(mem.size)38 offset2 = bitoffset(offset, 8)39 if offset2 !=offset:40 print "structural is not 8 bit aligned with end. need bit before end". \41 format(stview.name, offset2 -offset)42 returnfalse43 returntrue44
46 def align8bit(stview=stview()):47 """
48 8位元組自動填充49 :param stview:50 :return:51 """
52 flag = index = offset =053 while index
59 else:60 offset += stview.members[index].number *int(stview.members[index].size)61 index += 1
62 offset2 = bitoffset(offset, 8)63 if offset2 !=offset:64 align_mem = stmemberview("filled_".format(flag), "char", 1, offset2 -offset)65 stview.members.insert(index + 1, align_mem)66 returntrue67
69 if __name__ == "__main__":70 st =stview()71 """
72 例如結構體:73 tt74 81 """
83 st.name = "tt"
89 check8bit(st)90
91 align8bit(st)92 for mem inst.members:93 print mem.name, mem.type, mem.size, mem.number
執行結構如下:
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
include pragma pack 2 struct t.pragma pack int main int argc,char argv 最後輸出的結果為 8。這個表示是按照2位元組來對齊資料,首先分配2位元組給成員變數i,分配完成後,還剩一位元組,zj add補0 沒法容納成員變數d,此時會再...
結構體位元組對齊
在用sizeof運算子求算某結構體所佔空間時,並不是簡單地將結構體中所有元素各自佔的空間相加,這裡涉及到記憶體位元組對齊的問題。從理論上講,對於任何 變數的訪問都可以從任何位址開始訪問,但是事實上不是如此,實際上訪問特定型別的變數只能在特定的位址訪問,這就需要各個變數在空間上按一定的規則排列,而不是...