-
字節對齊意義
在進行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字節對齊)
# !/usr/bin/python # coding=UTF-8 BITOFFSET = lambda x, align: (x + (align - 1)) & ~(align - 1) class STView(object): def __init__(self): self.name = None # 結構體名 self.members = [] # 結構體成員,STMemberView對象 class STMemberView(object): def __init__(self, name=None, type_=None, size=None, number=1): self.name = name # 結構體成員名 self.type = type_ # 結構體成員類型 self.size = size # 結構體成員大小 self.number = number # 結構體成員數組大小 def check8bit(stView=STView()): """ 8字節檢測 :param stView: :return: """ offset = 0 for mem in stView.members: offset2 = BITOFFSET(offset, int(mem.size)) if offset2 != offset: print("structural {0} is not 8 bit aligned with {1}. need {2} bit before {1}". \ format(stView.name, mem.name, offset2 - offset)) return False else: offset += mem.number * int(mem.size) offset2 = BITOFFSET(offset, 8) if offset2 != offset: print("structural {0} is not 8 bit aligned with {1}. need {2} bit before {1}". \ format(stView.name, mem.name, offset2 - offset)) return False return True def align8bit(stView=STView()): """ 8字節自動填充 :param stView: :return: """ st_view_out = STView() st_view_out.name = stView.name flag = index = offset = 0 while index < len(stView.members): offset2 = BITOFFSET(offset, int(stView.members[index].size)) if offset2 != offset: align_mem = STMemberView("filled_{0}".format(flag), "char", 1, offset2 - offset) st_view_out.members.append(align_mem) flag += 1 offset = offset2 st_view_out.members.append(stView.members[index]) offset += stView.members[index].number * int(stView.members[index].size) index += 1 # 末尾8字節補齊 offset2 = BITOFFSET(offset, 8) if offset2 != offset: align_mem = STMemberView("filled_{0}".format(flag), "char", 1, offset2 - offset) st_view_out.members.append(align_mem) return st_view_out if __name__ == "__main__": st = STView() """ 例如結構體: tt { char m1; //此處差3字節 int m2; float m3; //末尾不對齊 } """ st.name = "tt" st.members.append(STMemberView('m1', "char", 1)) st.members.append(STMemberView('m2', "int", 4)) st.members.append(STMemberView('m3', "float", 4)) check8bit(st) st_out = align8bit(st) for mem in st_out.members: print(mem.name, mem.type, mem.size, mem.number)
執行結構如下:
如需轉載,請標明出處,謝謝