八字節對齊


  • 字節對齊意義

  在進行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)
        

 

 執行結構如下:

 

 如需轉載,請標明出處,謝謝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM