【C語言】字節對齊(內存對齊)


數據對齊 

1)平台原因(移植原因):不是所有的硬件平台都能訪問任意地址上的任意數據,某些硬件平台只能在某些地址處取某些特定類型的數據,否則拋出硬件異常
2)硬件原因:經過內存對齊之后,CPU的內存訪問速度大大提升。


1.  對齊原則:

【原則1】數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中,比較小的那個進行。

【原則2】結構(或聯合)的整體對齊規則:在數據成員完成各自對齊之后,結構(或聯合)本身也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。

【原則3】結構體作為成員:如果一個結構里有某些結構體成員,則結構體成員要從其內部最大元素大小的整數倍地址開始存儲。

備注:數組成員按長度按數組類型長度計算,如char t[9],在第1步中數據自身長度按1算,累加結構體時長度為9;第2步中,找最大數據長度時,如果結構體T有復雜類型成員A,該A成員的長度為該復雜類型成員A的最大成員長度。

小結:當#pragma pack的n值等於或超過所有數據成員長度的時候,這個n值的大小將不產生任何效果。


 【注意(對齊位數跟處理器位數和編譯器都有關)VS, VC等編譯器默認是#pragma pack(8),所以測試我們的規則會正常;注意gcc默認是#pragma pack(4),並且gcc只支持1,2,4對齊。套用三原則里計算的對齊值是不能大於#pragma pack指定的n值。

2. 自然對齊存放變量的地址要是該變量數據類型大小的整數倍。如:存放int型數據的地址一定要是4的倍數,存放short型數據的地址一定要是2的倍數。

 

3. 改變缺省的對界條件(指定對界):

  • 使用偽指令#pragma pack (n),C編譯器將按照n個字節對齊。
  • 使用偽指令#pragma pack(),取消自定義字節對齊方式。

舉例一

例1:

#pragma pack(1)
struct AA 
{
    int a;   //長度4 < 1 按1對齊;偏移量為0;存放位置區間[0,3]
    char b;  //長度1 = 1 按1對齊;偏移量為4;存放位置區間[4]
    short c; //長度2 > 1 按1對齊;偏移量為5;存放位置區間[5,6]
    char d;  //長度1 = 1 按1對齊;偏移量為6;存放位置區間[7]
    //整體存放在[0~7]位置區間中,共八個字節。
};
#pragma pack()

 結果:8個字節

 

例2:

#pragma pack(2)
struct AA
{
    int a;   //長度4 > 2 按2對齊;偏移量為0;存放位置區間[0,3]
    char b;  //長度1 < 2 按1對齊;偏移量為4;存放位置區間[4]
    short c; //長度2 = 2 按2對齊;偏移量要提升到2的倍數6;存放位置區間[6,7]
    char d;  //長度1 < 2 按1對齊;偏移量為7;存放位置區間[8];共九個字節
};
#pragma pack()

 結果:10個字節

 

例3:

#pragma pack(4)
struct AA 
{
    int a;   //長度4 = 4 按4對齊;偏移量為0;存放位置區間[0,3]
    char b;  //長度1 < 4 按1對齊;偏移量為4;存放位置區間[4]
    short c; //長度2 < 4 按2對齊;偏移量要提升到2的倍數6;存放位置區間[6,7]
    char d;  //長度1 < 4 按1對齊;偏移量為7;存放位置區間[8];總大小為9
};
#pragma pack()

 結果:12個字節

 

例4:

#pragma pack(8)
struct AA 
{
    int a;   //長度4 < 8 按4對齊;偏移量為0;存放位置區間[0,3]
    char b;  //長度1 < 8 按1對齊;偏移量為4;存放位置區間[4]
    short c; //長度2 < 8 按2對齊;偏移量要提升到2的倍數6;存放位置區間[6,7]
    char d;  //長度1 < 8 按1對齊;偏移量為7;存放位置區間[8],總大小為9
};
#pragma pack()

 結果:12個字節  

 

例5:

struct EE  //8個字節對齊
{
    int a;      //長度4 < 8 按4對齊;偏移量為0;存放位置區間[0,3]
    char b;     //長度1 < 8 按1對齊;偏移量為4;存放位置區間[4]
    short c;    //長度2 < 8 按2對齊;偏移量由5提升到6;存放位置區間[6,7]

    struct FF   //結構體內部最大元素為int,由於偏移量為8剛好是4的整數倍,所以從8開始存放接下來的struct FF
    {
        int a1;     //長度4 < 8 按4對齊;偏移量為8;存放位置區間[8,11]
        char b1;    //長度1 < 8 按1對齊;偏移量為12;存放位置區間[12]
        short c1;   //長度2 < 8 按2對齊;偏移量為13,提升到2的倍數14;存放位置區間[14,15]
        char d1;    //長度1 < 8 按1對齊;偏移量為16;存放位置區間[16]
    };              //整體對齊系數 = min((max(int,short,char), 8) = 4,將內存大小由17補齊到4的整數倍20
    
    char d;         //長度1 < 8 按1對齊;偏移量為21;存放位置區間[21]
                    //整體對齊系數 = min((max(int,short,char), 8) = 4,將內存大小由21補齊到4的整數倍24
};

 

struct B 
{
    char e[2];      //長度1 < 8 按2對齊;偏移量為0;存放位置區間[0,1]
    short h;        //長度2 < 8 按2對齊;偏移量為2;存放位置區間[2,3]
                    //結構體內部最大元素為double,偏移量為4,提升到8,所以從8開始存放接下來的struct A
    struct A 
    {
        int a;      //長度4 < 8 按4對齊;偏移量為8;存放位置區間[8,11]
        double b;   //長度8 = 8 按8對齊;偏移量為12,提升到16;存放位置區間16,23]
        float c;    //長度4 < 8,按4對齊;偏移量為24,存放位置區間[24,27]
    };
    //整體對齊系數 = min((max(int,double,float), 8) = 8,將內存大小由28補齊到8的整數倍32
};

舉例二

代碼1:

#include <stdio.h>
typedef struct
{
	int aa1;   //4個字節對齊 1111  
	char bb1;  //1個字節對齊 1  
	short cc1; //2個字節對齊 011  
	char dd1;  //1個字節對齊 1  
}testlength1;
int length1 = sizeof(testlength1); //4個字節對齊,占用字節1111 1011 1000,length = 12

typedef struct
{
	char bb2;  //1個字節對齊 1  
	int aa2;   //4個字節對齊 01111  
	short cc2; //2個字節對齊 11  
	char dd2;  //1個字節對齊 1  
}
testlength2; 
int length2 = sizeof(testlength2); //4個字節對齊,占用字節1011  1111 1000,length = 12

typedef struct
{
	char bb3;   //1個字節對齊 1  
	char dd3;   //1個字節對齊 1  
	int aa3;    //4個字節對齊 001111  
	short cc23; //2個字節對齊 11  
}testlength3;
int length3 = sizeof(testlength3); //4個字節對齊,占用字節1100 1111 1100,length = 12

typedef struct 
{
	char bb4;  //1個字節對齊 1  
	char dd4;  //1個字節對齊 1  
	short cc4; //2個字節對齊 11  
	int aa4;   //4個字節對齊 1111  
} testlength4;
int length4 = sizeof(testlength4); //4個字節對齊,占用字節1111 1111,length = 8

int main(void)
{
    printf("length1 = %d.\n",length1);
	printf("length2 = %d.\n", length2);
	printf("length3 = %d.\n", length3);
	printf("length4 = %d.\n", length4);
	return 0;
} 

VS2017輸出結果:

 

 代碼2:

#include <stdio.h>
#pragma pack(2)

typedef struct
{
	int aa1;   //2個字節對齊 1111  
	char bb1;  //1個字節對齊 1  
	short cc1; //2個字節對齊 011  
	char dd1;  //1個字節對齊 1  
} testlength1;
int length1 = sizeof(testlength1); //2個字節對齊,占用字節11 11 10 11 10,length = 10

typedef struct
{
	char bb2;    //1個字節對齊 1
	int aa2;     //2個字節對齊 01111  
	short cc2;   //2個字節對齊 11  
	char dd2;    //1個字節對齊 1  
} testlength2;
int length2 = sizeof(testlength2); //2個字節對齊,占用字節10 11 11 11 10,length = 10

typedef struct
{
	char bb3;   //1個字節對齊 1  
	char dd3;   //1個字節對齊 1  
	int aa3;    //2個字節對齊 11 11  
	short cc23; //2個字節對齊 11  
}testlength3;
int length3 = sizeof(testlength3); //2個字節對齊,占用字節11 11 11 11,length = 8

typedef struct
{
	char bb4;  //1個字節對齊 1  
	char dd4;  //1個字節對齊 1  
	short cc4; //2個字節對齊 11  
	int aa4;   //2個字節對齊 11 11 
}testlength4;
int length4 = sizeof(testlength4); //2個字節對齊,占用字節11 11 11 11,length = 8

int main(void)
{
	printf("length1 = %d.\n", length1);
	printf("length2 = %d.\n", length2);
	printf("length3 = %d.\n", length3);
	printf("length4 = %d.\n", length4);
	return 0;
}

VS2017輸出結果:

  

代碼3:

#include<iostream>
using namespace std;

typedef struct bb
{
	int id;             //[0]....[3]
	double weight;      //[8].....[15]      原則1
	float height;       //[16]..[19],總長要為8的整數倍,補齊[20]...[23]     原則3
}BB;

typedef struct aa
{
	char name[2];    //[0],[1]
	int  id;         //[4]...[7]          原則1
	double score;    //[8]....[15]    
	short grade;     //[16],[17]        
	BB b;            //[24]......[47]          原則2
}AA;

int main()
{
	AA a;
	cout << sizeof(a) << " " << sizeof(BB) << endl;
	return 0;
}

VS2017輸出結果: 48 24

 

代碼4:

#include<iostream>
using namespace std;

#pragma pack(2)
typedef struct bb
{
	int id;             
	double weight;      
	float height;       
}BB;

typedef struct aa
{
	char name[2];    
	int  id;        
	double score;    
	short grade;         
	BB b;           
}AA;

int main()
{
	AA a;
	cout << sizeof(a) << " " << sizeof(BB) << endl;
	return 0;
}

VS2017輸出結果:32 16  

 

參考資料

1. 5分鍾搞定內存字節對齊
2.  快速理解字節對齊問題
3.  關於面試題中結構體內存對齊計算總結

 

 

 


免責聲明!

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



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