結構體的內存對齊

1、爲何要內存對齊

爲了高速處理數據,現代處理器的設計引入了對齊的概念。所謂對齊就是保證數據在內存中存儲時地址變化按照必定的規律,這樣就能夠保證每次cpu取一樣長度的數據進行運算,所以能夠提升計算機的運行速度。spa

2、什麼是內存對齊

下面的程序演示內存中的對齊:設計

#include <stdio.h>

struct test{
  char ch;
  short s;
  int i;
};

int main(void)
{
  struct test var;
  printf("size of var : %d\n", sizeof(var));
  return 0;
}

運行結果爲:code

該結構體擁有一個char型、一個short型和一個int型成員變量,所佔用的字節數應該是7個,可是結果倒是8個字節,這是由於編譯器採用了默認的對齊方式。內存

3、內存對齊的一些概念

1.數據類型自身的對齊值

基本數據類型的自身對齊值爲其數據類型的大小。編譯器

2.指定對齊值

默認的指定對齊值爲成員中自身對齊值最大的那個值。io

使用下面語句能夠指定對齊值value:編譯

#progma pack (value) /*指定按value字節對齊*/
struct A {
    int a;
};
#progma pack () /*取消指定對齊,恢復缺省對齊*/

3.結構體或者類的自身對齊值

其成員中自身對齊值最大的那個值。class

4.數據類型、結構體和類的有效對齊值

自身對齊值和指定對齊值中較小的那個值。test

4、內存對齊的實現

設結構體以下:變量

struct test{
  char ch;
  int i;
  short s; 
};

假設test的起始地址爲0x0000,默認的指定對齊值爲4,因此:

  1. 第一個成員變量ch,自身對齊值爲1,小於指定對齊值4,因此有效對齊值爲1,ch的存放地址爲0x0000,而且0x0000是自身對齊值1的倍數。
  2. 第二個成員變量i,自身對齊值是4,等於指定對齊值4,因此有效對齊值爲4,而變量起始地址應爲自身有效對齊值的倍數,因此i應存放在0x0004到0x0007這四個連續的字節空間。
  3. 第三個成員變量s,自身對齊值爲2,小於指定對齊值爲4,因此有效對齊值爲2,變量起始存放地址應爲2的倍數,因此s存放在0x0008到0x0009兩個字節空間。
  4. 接下來看結構體test自身對齊值爲其變量中最大對齊值(這裏是i)因此就是4,因此結構體的有效對齊值也是4,結構體的大小應爲結構體有效對齊值的倍數,因此因此0x0000A到0x000B也被結構體所佔用,0x0000到0x000B共12個字節,這就是結構體test所佔內存大小。

當咱們指定對齊值爲2字節時,再分析上面的結構體

#pragma pack(2)  //指定按2字節對齊
struct test1{
  char ch;
  int i;
  short s; 
};
#pragma pack()  //取消指定對齊,恢復缺省對齊

 假設test1的起始地址爲0x0000,指定對齊值爲2,因此:

  1. 第一個成員變量ch,自身對齊值爲1,小於指定對齊值爲2,因此有效對齊值爲1,ch存放在0x0000。
  2. 第二個成員變量i,自身對齊值爲4,大於指定對齊值,因此有效對齊值爲2,存放的首地址爲有效對齊值2的倍數即0x0002,所以i存放在0x0002,0x0003,0x0004,0x0005四個連續的字節空間。
  3. 第三個成員變量s,自身對齊值爲2,等於指定對齊值,因此有效對齊值爲2,存放首地址爲2的倍數即0x0006,所以s存放在0x0006,0x0007兩個字節空間。
  4. 結構體test1自身對齊值即最大成員變量自身對齊值4,大於指定對齊值2,因此有效對齊值爲2,而結構體大小爲0x0000到0x0007共8個字節,是2的倍數,所以結構體test1的內存大小爲8字節

5、分析幾個程序

1.程序以下:

#include <stdio.h>

#pragma pack(8)
struct example1
{
  short a;
  long b;
};
struct example2
{
  char c;
  struct example1 struct1;
  short e;
};
#pragma pack()

int main(int argc, char* argv[])
{
  struct example2 struct2;
  struct example1 e1;
  printf("size of example1 : %d\n", sizeof(e1));
  printf("size of example2 : %d\n", sizeof(struct2));
  printf("%d\n", (unsigned long int)(&struct2.struct1) - (unsigned long int)(&struct2));

  return 0;
}

程序的輸出結果是什麼?

答案是:

分析這個程序:

對於example1:

  1. 成員變量a,自身對齊值爲2,指定對齊值爲8,因此有效對齊值爲2,所以a佔2個字節空間。
  2. 成員變量b,自身對齊值爲8,等於指定對齊值,因此有效對齊值爲8,起始地址應爲8的倍數,所以須要填充6個字節,b佔8個字節空間。
  3. 結構體的有效對齊值爲8,而成員變量共佔16個字節,是8的倍數,所以結構體example1共佔16個字節空間。

對於example2:

  1. 成員變量c,自身對齊值爲1,小於指定對齊值,因此有效對齊值爲1,佔1個字節空間。
  2. 成員struct1是一個example1結構體,該結構體的自身對齊值爲8,等於指定對齊值,因此起始地址爲8的倍數,所以填充7個字節,佔16個字節空間。
  3. 成員e,自身對齊值2,小於指定對齊值,有效對齊值爲2,佔兩個字節大小。
  4. 結構體多有成員所佔大小爲:1+7+16+2=26,而結構體的有效對齊值爲8,所以結構體需佔32個字節空間(8的倍數)。 

 對於(unsigned long int)(&struct2.struct1) - (unsigned long int)(&struct2):

由於在example2中struct1以前有個char類型的成員變量,因爲struct1的對齊值爲8因此char變量須要填充7個字節,所以結果爲8。

2.第二個程序:

#include <stdio.h>\

union A
{
  int a[5];
  char b;
  double c;
};

struct B
{
  int n;
  union A a;
  char c[10];
};

int main(void)
{
  union A a1;
  struct B b;
  printf("size of union : %d\n", sizeof(a1));
  printf("size of struct : %d\n", sizeof(b));
  return 0;
}

A和B的大小爲:

分析程序:

因爲union是共享內存的,首先看每一個成員所佔大小:

union A
{
  int a[5];  //20字節
  char b;    //1字節
  double c;  //8字節
};

A中各變量默認內存對齊方式,必須以最長的double 8字節對齊,因此A的大小爲24字節(20+4)。

結構體B中:

struct B
{
  int n;      //4字節
  union A a;  //24字節
  char c[10]; //10字節
};

因爲A是8字節對齊的,因此B中的int與char[]也須要8字節對齊,因此大小爲:8+24+16 = 48。

相關文章
相關標籤/搜索