程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C說話內存對齊實例詳解

C說話內存對齊實例詳解

編輯:關於C++

C說話內存對齊實例詳解。本站提示廣大學習愛好者:(C說話內存對齊實例詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是C說話內存對齊實例詳解正文


本文具體講述了C說話法式設計中內存對其的概念與用法。分享給年夜家供年夜家參考之用。詳細以下:

1、字節對齊根本概念

古代盤算機中內存空間都是依照byte劃分的,從實際上講仿佛對任何類型的變量的拜訪可以從任何地址開端,但現實情形是在拜訪特定類型變量的時刻常常在特定的內存地址拜訪,這就須要各類類型數據依照必定的規矩在空間上分列,而不是次序的一個接一個的排放,這就是對齊。 對齊的感化和緣由:各個硬件平台對存儲空間的處置上有很年夜的分歧。一些平台對某些特定類型的數據只能從某些特定地址開端存取。好比有些架構的CPU在拜訪一個沒有停止對齊的變量的時刻會產生毛病,那末在這類架構下編程必需包管字節對齊.其他平台能夠沒有這類情形,然則最多見的是假如不依照合適其平台請求對數據寄存停止對齊,會在存取效力上帶來喪失。好比有些平台每次讀都是從偶地址開端,假如一個int型(假定為32位體系)假如寄存在偶地址開端的處所,那 麼一個讀周期便可以讀出這32bit,而假如寄存在奇地址開端的處所,就須要2個讀周期,並對兩次讀出的成果的高下字節停止拼集能力獲得該32bit數據。明顯在讀取效力高低降許多。

請看上面的構造:

struct struct1 
{ 
  double dda; 
  char cda; 
  int ida; 
}; 
sizeof(struct1) = ?

毛病的求法:

sizeof(struct1)=sizeof(double)+sizeof(char)+sizeof(int)=13

然則當你運轉以下測試代碼:

#include<stdio.h>
struct mystruct
{
  double dda;
  char cda;
  int ida;
};
int main()
{
  struct mystruct ss;
  printf("%d\n",sizeof(ss));
  return 0;
}

運轉成果為:16

其實,這是編譯器對變量存儲的一個特別處置。為了進步CPU的存儲速度,編譯器對一些變量的肇端地址做了“對齊”處置。在默許情形下,編譯器劃定各成員變量寄存的肇端地址絕對於構造的肇端地址的偏移量必需為該變量的類型所占用的字節數的倍數。上面列出經常使用類型的對齊方法:

類型            對齊方法(變量寄存的肇端地址絕對於構造的肇端地址的偏移量)

char              偏移量必需為sizeof(char)即1的倍數

int                偏移量必需為sizeof(int)即4的倍數

float             偏移量必需為sizeof(float)即4的倍數

double          偏移量必需為sizeof(double)即8的倍數

Short            偏移量必需為sizeof(short)即2的倍數

各成員變量在寄存的時刻依據在構造中湧現的次序順次請求空間,同時依照下面的對齊方法調劑地位,空白的字節編譯器會主動填充。同時編譯器為了確保構造的年夜小為構造的字節界限數(即該構造中占用最年夜空間的類型所占用的字節數)的倍數,所以在為最初一個成員變量請求空間後,還會依據須要主動填充空白的字節

如今來剖析編譯器是如何來寄存構造的:

struct struct1 
{ 
  double dda; 
  char cda; 
  int ida; 
}; 

第一個成員dda分派空間,其肇端地址跟構造的肇端地址雷同(偏移量0恰好為sizeof(double)的倍數),該成員變量占用sizeof(double)=8個字節;接上去為第二個成員cda分派空間,這時候下一個可以分派的地址關於構造的肇端地址的偏移量為8,是sizeof(char)的倍數,所以把cda寄存在偏移量為8的處所知足對齊方法,該成員變量占用 sizeof(char)=1個字節;接上去為第三個成員ida分派空間,這時候下一個可以分派的地址關於構造的肇端地址的偏移量為9,不是sizeof (int)=4的倍數,為了知足對齊方法對偏移量的束縛成績,VC主動填充3個字節(這三個字節沒有放甚麼器械),這時候下一個可以分派的地址關於構造的肇端地址的偏移量為12,恰好是sizeof(int)=4的倍數,所以把ida寄存在偏移量為12的處所,該成員變量占用sizeof(int)=4個字節;這時候全部構造的成員變量曾經都分派了空間,總的占用的空間年夜小為:8+1+3+4=16,恰好為構造的字節界限數(即構造中占用最年夜空間的類型所占用的字節數sizeof(double)=8)的倍數,沒有空白的字節須要填充。所以全部構造的年夜小為:sizeof(struct1)=8+1+ 3+4=16,個中有3個字節是VC主動填充的,沒有聽任何成心義的器械。

上面再舉個例子,交流一下下面的struct1的成員變量的地位,使它釀成上面的情形:

struct mystruct2
{
  char cda;
  double dda;
  int ida;
};

 運轉成果為:24

struct mystruct2
{
  char cda;  //偏移量為0,知足對齊方法,cda占用1個字節;
  double dda; //下一個可用的地址的偏移量為1,不是sizeof(double)=8 
         //的倍數,須要補足7個字節能力使偏移質變為8(知足對齊 
         //方法),是以VC主動填充7個字節,dda寄存在偏移量為8 
         //的地址上,它占用8個字節。 

  int ida;   //下一個可用的地址的偏移量為16,是sizeof(int)=4的倍 
         //數,知足int的對齊方法,所以不須要VC主動填充,type存 
         //放在偏移量為16的地址上,它占用4個字節。
  
  //一切成員變量都分派了空間,空間總的年夜小為1+7+8+4=20,不是構造 
  //的節界限數(即構造中占用最年夜空間的類型所占用的字節數sizeof 
  //(double)=8)的倍數,所以須要填充4個字節,以知足構造的年夜小為 
  //sizeof(double)=8的倍數。
};

所以該構造總的年夜小為:sizeof(struct2)為1+7+8+4+4=24。個中總的有7+4=11個字節是VC主動填充的,沒有聽任何成心義的器械。

2、#pragma pack(n)來設定變量以n字節對齊方法

VC對構造的存儲的特別處置確切進步CPU存儲變量的速度,然則有時刻也帶來了一些費事,我們也屏障失落變量默許的對齊方法,本身可以設定變量的對齊方法。VC 中供給了#pragma pack(n)來設定變量以n字節對齊方法。n字節對齊就是說變量寄存的肇端地址的偏移量有兩種情形:

第1、假如n年夜於等於該變量所占用的字節數,那末偏移量必需知足默許的對齊方法;

第2、假如n小於該變量的類型所占用的字節數,那末偏移量為n的倍數,不消知足默許的對齊方法。

構造的總年夜小也有個束縛前提,分上面兩種情形:假如n年夜於一切成員變量類型所占用的字節數,那末構造的總年夜小必需為占用空間最年夜的變量占用的空間數的倍數;不然必需為n的倍數。上面舉例解釋其用法:

#pragma pack(push) //保留對齊狀況 
#pragma pack(4)//設定為4字節對齊 
struct test 
{ 
  char m1; 
  double m4; 
  int m3; 
}; 
#pragma pack(pop)//恢復對齊狀況 

以上構造的年夜小為16,上面剖析其存儲情形,起首為m1分派空間,其偏移量為0,知足我們本身設定的對齊方法(4字節對齊),m1占用1個字節。接著開端為 m4分派空間,這時候其偏移量為1,須要補足3個字節,如許使偏移量知足為n=4的倍數(由於sizeof(double)年夜於n),m4占用8個字節。接著為m3分派空間,這時候其偏移量為12,知足為4的倍數,m3占用4個字節。這時候曾經為一切成員變量分派了空間,共分派了4+8+4=16個字節,知足為n的倍數。假如把下面的#pragma pack(4)改成#pragma pack(16),那末我們可以獲得構造的年夜小為24。

再看上面這個例子:

#pragma pack(8)
struct S1{
  char a;
  long b;
};
struct S2 {
  char c;
  struct S1 d;
  long long e;
};
#pragma pack()

成員對齊有一個主要的前提,即每一個成員分離對齊.即每一個成員按本身的方法對齊.

也就是說下面固然指定了按8字節對齊,但其實不是一切的成員都是以8字節對齊.其對齊的規矩是,每一個成員按其類型的對齊參數(平日是這個類型的年夜小)和指定對齊參數(這裡是8字節)中較小的一個對齊.而且構造的長度必需為所用過的一切對齊參數的整數倍,不敷就補空字節.

S1中,成員a是1字節默許按1字節對齊,指定對齊參數為8,這兩個值中取1,a按1字節對齊;成員b是4個字節,默許是按4字節對齊,這時候就按4字節對齊,所以sizeof(S1)應當為8;

S2 中,c和S1中的a一樣,按1字節對齊,而d 是個構造,它是8個字節,它按甚麼對齊呢?關於構造來講,它的默許對齊方法就是它的一切成員應用的對齊參數中最年夜的一個,S1的就是4.所以,成員d就是按4字節對齊.成員e是8個字節,它是默許按8字節對齊,和指定的一樣,所以它對到8字節的界限上,這時候,曾經應用了12個字節了,所以又添加了4個字節的空,從第16個字節開端放置成員e.這時候,長度為24,曾經可以被8(成員e按8字節對齊)整除.如許,sizeof(S2)為24個字節.

這裡有三點很主要:

1.每一個成員分離按本身的方法對齊,並能最小化長度。

2.龐雜類型(如構造)的默許對齊方法是它最長的成員的對齊方法,如許在成員是龐雜類型時,可以最小化長度。

3.對齊後的長度必需是成員中最年夜的對齊參數的整數倍,如許在處置數組時可以包管每項都界限對齊。

3、minix的stdarg.h文件中對齊方法

在minix的stdarg.h文件中,界說了以下一個宏:

/* Amount of space required in an argument list for an arg of type TYPE.
 * TYPE may alternatively be an expression whose type is used.
 */

#define __va_rounded_size(TYPE) \
 (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))

從正文和宏的名字可以看出是有關內存對齊方面的感化。依據後面關於C說話內存對齊方面的實際可知

n字節對齊就是說變量寄存的肇端地址的偏移量有兩種情形:

第1、假如n年夜於等於該變量所占用的字節數,那末偏移量必需知足默許的對齊方法(各成員變量寄存的肇端地址絕對於構造的肇端地址的偏移量必需為該變量的類型所占用的字節數的倍數);

第2、假如n小於該變量的類型所占用的字節數,那末偏移量為n的倍數,不消知足默許的對齊方法。

此時n = 4,關於sizeof(TYPE)必定為天然數,sizeof(int) - 1 = 3

sizeof(TYPE)只能夠湧現以下兩種情形:

(1) 當sizeof(TYPE) >= 4,偏移量 = (sizeof(TYPE)/4)*4

(2) 當sizeof(TYPE) < 4,偏移量 = 4

此時sizeof(TYPE) = 1 or 2 or 3,而(sizeof(TYPE) + 3) / 4  = 1

為了將上述兩種情形同一,偏移量 = ((sizeof(TYPE) + 3) / 4) * 4

 在有的源代碼中,將內存對齊宏__va_rounded_size經由過程位操作來完成,代碼以下:

#define __va_rounded_size(TYPE) \
  ((sizeof(TYPE)+sizeof(int)-1)&~(sizeof(int)-1))

因為 ~(sizeof(int) – 1) ) = ~(4-1)=~(00000011B)=11111100B

(sizeof(TYPE) + sizeof(int) – 1)就是將年夜於4m但小於等於4(m+1)的數進步到年夜於等於4(m+1)但小於4(m+2),如許再& ~(sizeof(int) – 1) )後就正好將原長度補齊到4的倍數了。

信任本文所述對年夜家C法式設計的進修必定的自創價值。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved