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

深刻懂得C說話內存對齊

編輯:關於C++

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


一.內存對齊的初步講授

內存對齊可以用一句話來歸納綜合:

“數據項只能存儲在地址是數據項年夜小的整數倍的內存地位上”

例如int類型占用4個字節,地址只能在0,4,8等地位上。

例1:

#include <stdio.h>
struct xx{
        char b;
        int a;
        int c;
        char d;
};

int main()
{
        struct xx bb;
        printf("&a = %p/n", &bb.a);
        printf("&b = %p/n", &bb.b);
        printf("&c = %p/n", &bb.c);
        printf("&d = %p/n", &bb.d);
        printf("sizeof(xx) = %d/n", sizeof(struct xx));

        return 0;
}

履行成果以下:

&a = ffbff5ec
&b = ffbff5e8
&c = ffbff5f0
&d = ffbff5f4
sizeof(xx) = 16

會發明b與a之間空出了3個字節,也就是說在b以後的0xffbff5e9,0xffbff5ea,0xffbff5eb空了出來,a直接存儲在了0xffbff5ec, 由於a的年夜小是4,只能存儲在4個整數倍的地位上。打印xx的年夜小會發明,是16,有些人能夠要問,b以後空出了3個字節,那也應當是13啊?其他的3個 呢?這個往後浏覽本文會懂得的更深刻一點,這裡簡略說一下就是d後邊的3個字節,也會糟蹋失落,也就是說,這3個字節也被這個構造體占用了.

可以簡略的修正構造體的構造,來下降內存的應用,例如可以將構造體界說為:

struct xx{
        char b;
        char d;
        int a;         
        int c;                 
};

如許打印這個構造體的年夜小就是12,省了許多空間,可以看出,在界說構造體的時刻,必定要斟酌要內存對齊的影響,如許能使我們的法式占用更小的內存。

二.操作體系的默許對齊系數

每 個操作體系都有本身的默許內存對齊系數,假如是新版本的操作體系,默許對齊系數普通都是8,由於操作體系界說的最年夜類型存儲單位就是8個字節,例如 long long(為何必定要如許,在第三節會講授),不存在跨越8個字節的類型(例如int是4,char是1,long在32位編譯時是4,64位編譯時是 8)。當操作體系的默許對齊系數與第一節所講的內存對齊的實際發生抵觸時,以操作體系的對齊系數為基准。

例如:

假定操作體系的默許對齊系數是4,那末對與long long這個類型的變量就不知足第一節所說的,也就是說long long這類構造,可以存儲在被4整除的地位上,也能夠存儲在被8整除的地位上。

可以經由過程#pragma pack()語句修正操作體系的默許對齊系數,編寫法式的時刻不建議修正默許對齊系數,在第三節會講授緣由

例2:

#include <stdio.h>
#pragma pack(4)
struct xx{
        char b;
        long long a;
        int c;
        char d;
};
#pragma pack()

int main()
{
        struct xx bb;
        printf("&a = %p/n", &bb.a);
        printf("&b = %p/n", &bb.b);
        printf("&c = %p/n", &bb.c);
        printf("&d = %p/n", &bb.d);
        printf("sizeof(xx) = %d/n", sizeof(struct xx));

        return 0;
}

打印成果為:

&a = ffbff5e4
&b = ffbff5e0
&c = ffbff5ec
&d = ffbff5f0
sizeof(xx) = 20

發明占用8個字節的a,存儲在了不克不及被8整除的地位上,存儲在了被4整除的地位上,采用了操作體系的默許對齊系數。

三.內存對齊發生的緣由

內存對齊是操作體系為了疾速拜訪內存而采用的一種戰略,簡略來講,就是為了放置變量的二次拜訪。操作體系在拜訪內存 時,每次讀取必定的長度(這個長度就是操作體系的默許對齊系數,或許是默許對齊系數的整數倍)。假如沒有內存對齊時,為了讀取一個變量是,會發生總線的二 次拜訪。

例如假定沒有內存對齊,構造體xx的變量地位會湧現以下情形:

struct xx{
        char b;         //0xffbff5e8
        int a;            //0xffbff5e9      
        int c;             //0xffbff5ed     
        char d;         //0xffbff5f1
};

操作體系先讀取0xffbff5e8-0xffbff5ef的內存,然後在讀取0xffbff5f0-0xffbff5f8的內存,為了取得值c,就須要將兩組內存歸並,停止整合,如許嚴重下降了內存的拜訪效力。(這就觸及到了陳詞濫調的成績,空間和效力哪一個更主要?這裡不做評論辯論)。

如許年夜家就可以懂得為何構造體的第一個變量,不論類型若何,都是能被8整除的吧(由於拜訪內存是從8的整數倍開端的,為了增長讀取的效力)!

內存對齊的成績重要存在於懂得struct等復合構造在內存中的散布。

起首要明確內存對齊的概念。
很多現實的盤算機體系對根本類型數據在內存中寄存的地位無限制,它們會請求這些數據的首地址的值是某個數k(平日它為4或8)的倍數,這就是所謂的內存對齊。

這個k在分歧的cpu平台下,分歧的編譯器下表示也有所分歧。好比32位字長的盤算機與16位字長的盤算機。這個離我們有些遠了。我們的開辟重要觸及兩年夜平台,windows和linux(unix),觸及的編譯器也重要是microsoft編譯器(如cl),和gcc。

內存對齊的目標是使各個根本數據類型的首地址為對應k的倍數,這是懂得內存對齊方法的最終寶貝。別的還要辨別編譯器的分離。明確了這兩點根本上就可以弄定一切內存對齊方面的成績。

分歧編譯器中的k:
1、關於microsoft的編譯器,每種根本類型的年夜小即為這個k。年夜體上char類型為8,int為32,long為32,double為64。
2、關於linux下的gcc編譯器,劃定年夜小小於等於2的,k值為其年夜小,年夜於等於4的為4。

明確了以上的解釋對struct等復合構造的內存散布就應當很清晰了。

上面看一下最簡略的一個類型:struct中成員都為根本數據類型,例如:

struct test1
{
char a;
short b;
int c;
long d;
double e;
};

在windows平台,microsoft編譯器下:

假定從0地址開端,起首a的k值為1,它的首地址可使隨意率性地位,所以a占用第一個字節,即地址0;然後b的k值為2,他的首地址必需是2的倍數,不克不及是1,所以地址1誰人字節被填充,b首地址為地址2,占用地址2,3;然後到c,c的k值為4,他的首地址為4的倍數,所以首地址為4,占用地址4,5,6,7;再然後到d,d的k值也為4,所以他的首地址為8,占用地址8,9,10,11。最初到e,他的k值為8,首地址為8的倍數,所以地址12,13,14,15被填充,他的首地址應為16,占用地址16-23。明顯其年夜小為24。

這就是 test1在內存中的散布情形。我們樹立一個test1類型的變量,a、b、c、d、e分離賦值2、4、8、16、32。然後從低地址順次打印出內存中每一個字節對應的16進制數為:
2 0 4 0 8 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 40 40

驗證:
明顯揣摸是准確的。

在linux平台,gcc編譯器下:
假定從0地址開端,起首a的k值為1,它的首地址可使隨意率性地位,所以a占用第一個字節,即地址0;然後b的k值為2,他的首地址必需是2的倍數,不克不及是1,所以地址1誰人字節被填充,b首地址為地址2,占用地址2,3;然後到c,c的k值為4,他的首地址為4的倍數,所以首地址為4,占用地址4,5,6,7;再然後到d,d的k值也為4,所以他的首地址為8,占用地址8,9,10,11。最初到e,從這裡開端與microsoft的編譯器開端有所差別,他的k值為不是8,依然是4,所以其首地址是12,占用地址12-19。明顯其年夜小為20。

驗證:
我們樹立一個test1類型的變量,a、b、c、d、e分離賦值2、4、8、16、32。然後從低地址順次打印出內存中每一個字節對應的16進制數為:
2 0 4 0 8 0 0 0 10 0 0 0 0 0 0 0 0 0 40 40

明顯揣摸也是准確的。

接上去,看一看幾類特別的情形,為了不費事,不再描寫內存散布,只盤算構造年夜小。

第一種:嵌套的構造

struct test2
{
char f;
struct test1 g;
};

在windows平台,microsoft編譯器下:

這類情形下假如把test2的第二個成員拆開來,研討內存散布,那末可以曉得,test2的成員f占用地址0,g.a占用地址1,今後的內存散布不變,依然知足一切根本數據成員的首地址都為其對應k的倍數這一准繩,那末test2的年夜小就照樣24了。然則現實上test2的年夜小為32,這是由於:不克不及由於test2的構造而轉變test1的內存散布情形,所認為了使test1種各個成員依然知足對齊的請求,f成員前面須要填充必定數目的字節,不難發明,這個數目應為7個,能力包管test1的對齊。所以test2絕對於test1來講增長了8個字節,所以test2的年夜小為32。

在linux平台,gcc編譯器下:

異樣,這類情形下假如把test2的第二個成員拆開來,研討內存散布,那末可以曉得,test2的成員f占用地址0,g.a占用地址1,今後的內存散布不變,依然知足一切根本數據成員的首地址都為其對應k的倍數這一准繩,那末test2的年夜小就照樣20了。然則現實上test2的年夜小為24,異樣這是由於:不克不及由於test2的構造而轉變test1的內存散布情形,所認為了使test1種各個成員依然知足對齊的請求,f成員前面須要填充必定數目的字節,不難發明,這個數目應為3個,能力包管test1的對齊。所以test2絕對於test1來講增長了4個字節,所以test2的年夜小為24。

第二種:位段對齊

struct test3
{
unsigned int a:4;
unsigned int b:4;
char c;
};

或許

struct test3
{
unsigned int a:4;
int b:4;
char c;
};

在windows平台,microsoft編譯器下:

相鄰的多個同類型的數(帶符號的與不帶符號的,只需根本類型雷同,也為雷同的數),假如他們占用的位數不跨越根本類型的年夜小,那末他們可作為一個全體來對待。分歧類型的數要遵守各自的對齊方法。
如:test3中,a、b可作為一個全體,他們作為一個int型數據來對待,所以test3的年夜小為8字節。而且a與b的值在內存中從低位開端順次分列,位於4字節區域中的前0-3位和4-7位

假如test4位以下格局

struct test4
{
unsigned int a:30;
unsigned int b:4;
char c;
};

那末test4的年夜小就為12個字節,而且a與b的值分離散布在第一個4字節的前30位,和第二個4字節的前4位。

如過test5是以下情勢

struct test5
{
unsigned int a:4;
unsigned char b:4;
char c;
};

那末因為int和char分歧類型,他們分離以各自的方法對齊,所以test5的年夜小應為8字節,a與b的值分離位於第一個4字節的前4位和第5個字節的前4位。

在linux平台,gcc編譯器下:

struct test3
{
unsigned int a:4;
unsigned int b:4;
char c;
};

gcc下,相鄰各成員,不論類型能否雷同,占的位數之和跨越這些成員中第一個的年夜小的時刻,在構造中以k值為1對齊,在構造外k值為其根本類型的值。不跨越的情形下在內存中順次分列。
如test3,其年夜小為4。a,b的值在內存中順次分列分離為第一個四字節中的0-3和4-7位。

假如test4位以下格局

struct test4
{
unsigned int a:20;
unsigned char b:4;
char c;
};

test4的年夜小為4個字節,而且a與b的值分離散布在第一個4字節的0-19位,和20-23位,c寄存在第4個字節中。
如過test5是以下情勢

struct test5
{
unsigned int a:10;
unsigned char b:4;
short c;
};

那末test5的年夜小應為4字節,a,b的值為0-9位和10-13位。c寄存在後兩個字節中。假如a的年夜小釀成了20
那末test5的年夜小應為8字節。即

struct test6
{
unsigned int a:20;
unsigned char b:4;
short c;
};

此時,test6的a、b共占用0,1,2共3字節,c的k值為2,其實可以4位首地位,然則在構造外,a要以int的方法對齊。也就是說持續兩個test6對象在內存中寄存的話,a的首地位要包管為4的倍數,那末c前面必需多填充2位。所以test6的年夜小為8個字節。

關於位段構造的部門是比擬龐雜的。臨時我就曉得這麼多。

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