linux 改變GCC編譯器的位元組對齊方式

llzhang_fly發表於2020-09-24

linux C  位元組對其簡介

在C語言中,結構是一種複合資料型別,其構成元素既可以是基本資料型別(如int、long、float等)的變數,也可以是一些複合資料型別(如陣列、結構、聯合等)的資料單元。

在結構中,編譯器為結構的每個成員按其自然對界(alignment)條件分配空間。各個成員按照它們被宣告的順序在記憶體中順序儲存,第一個成員的地址和整個結構的地址相同。

    例如,下面的結構各成員空間分配情況:
        struct test
        {
             char x1;
             short x2;
             float x3;
             char x4;
        };
    結構的第一個成員x1,其偏移地址為0,佔據了第1個位元組。
    第二個成員x2為short型別,其起始地址必須2位元組對界,因此,編譯器在x2和x1之間填充了一個空位元組。
    第三個成員x3和第四個成員x4恰好落在其自然對界地址上,在它們前面不需要額外的填充位元組。
    在test結構中,成員x3要求4位元組對界,是該結構所有成員中要求的最大對界單元,因而test結構的自然對界條件為4位元組,編譯器在成員x4後面填充了3個空位元組。整個結構所佔據空間為12位元組。

更改C編譯器的預設位元組對齊方式

在預設情況下,C編譯器為每一個變數或是資料單元按其自然對界條件分配空間。一般地,可以通過下面的方法來改變預設的對界條件:

方式一:
    · 使用偽指令#pragma pack (n),C編譯器將按照n個位元組對齊。
    · 使用偽指令#pragma pack (),取消自定義位元組對齊方式。

方式二:
    · __attribute((aligned (n))),讓所作用的結構成員對齊在n位元組自然邊界上。如果結構中有成員的長度大於n,則按照最大成員的長度來對齊。
    · __attribute__ ((packed)),取消結構在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊。

以上的n = 1, 2, 4, 8, 16... 第一種方式較為常見。

在網路協議程式設計中,經常會處理不同協議的資料包文。一種方法是通過指標偏移的方法來得到各種資訊,但這樣做不僅程式設計複雜,而且一旦協議有變化,程式修改起來也比較麻煩。在瞭解了編譯器對結構空間的分配原則之後,我們完全可以利用這一特性定義自己的協議結構,通過訪問結構的成員來獲取各種資訊。這樣做,不僅簡化了程式設計,而且即使協議發生變化,我們也只需修改協議結構的定義即可,其它程式無需修改,省時省力。下面以TCP協議首部為例,說明如何定義協議結構。其協議結構定義如下:

#pragma pack(1) // 按照1位元組方式進行對齊

對齊原則:

1、基本型別變數起始地址要按一定規則對齊.
    char 型別,其起始地址要1位元組邊界上,即其地址能被1整除(即任意地址即可)
    short型別,其起始地址要2位元組邊界上,即其地址能被2整除
    int  型別,其起始地址要4位元組邊界上,即其地址能被4整除
    long型別,其起始地址要4位元組邊界上,即其地址能被4整除
    float型別,其起始地址要4位元組邊界上,即其地址能被4整除
    double型別,其起始地址要8位元組邊界上,即其地址能被8整除  

    注意:指標型別,其起始地址要在8位元組邊界上

2、結構例項起始址要在自己最大尺寸成員的對齊地址上
   如最大尺寸的成員是short,則要基於2對齊

3、結構內成員的偏移量也要參照第1條,滿足相應倍數
    如成員是short,則偏移量也是2的倍數.
    這一條實際仍然是第1條規則的擴充套件,因為結構起始地址按最大倍數來,加上內部相應倍數,這樣成員絕對地址仍然滿足第1條規定

4、結構總尺寸也要對齊. 要為最大尺寸的成員的整數倍,
如果不是則要在結構最後補齊成整數倍

位元組對齊例項

#include <stdio.h>

// 使用系統預設的位元組對齊方式,32位機器一般為4位元組對齊;
struct person0{
    char *name;
    int age;
    char score;
    int id;
};

// 取消在編譯過程中的優化對齊,按照實際佔用位元組數進行對齊
struct person1{
    char *name;
    int age;
    char score;
    int id;
}__attribute__((packed));

// 構體成員對齊在4位元組邊界上
struct person2{
    char *name;
    int age;
    char score;
    int id;
} __attribute__((aligned (4)));

int main(int argc, char **argv)
{
    printf("size of (struct person0) = %lu.\n", sizeof(struct person0));
    printf("size of (struct person1) = %lu.\n", sizeof(struct person1));
    printf("size of (struct person2) = %lu.\n", sizeof(struct person2));

    return 0;
}

執行結果:

因64位系統,所以指標佔8位元組的資料記憶體,int 也是8位元組;

 

getconf命令可以獲取系統的基本配置資訊,比如作業系統位數,記憶體大小

檢視作業系統是多少位

 

相關文章