亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C語言、C++內存對齊問題詳解

2020-01-26 15:15:28
字體:
來源:轉載
供稿:網友

這也可以?

復制代碼 代碼如下:

#include <iostream>
using namespace std;
 
struct Test_A
{
     char a;
     char b;
     int c;
};
 
struct Test_B
{
     char a;
     int c;
     char b;
};
 
struct Test_C
{
     int c;
     char a;
     char b;
};
 
int main()
{
     struct Test_A a;
     memset(&a, 0, sizeof(a));
 
     struct Test_B b;
     memset(&b, 0, sizeof(b));
 
     struct Test_C c;
     memset(&c, 0, sizeof(c));
 
     // Print the memory size of the struct
     cout<<sizeof(a)<<endl;
     cout<<sizeof(b)<<endl;
     cout<<sizeof(c)<<endl;
 
     return 0;
}

好了,一段簡單的程序,上面的這段程序輸出是什么?如果你很懂,也就會知道我接下來要講什么了,可以略過了;如果,你不知道,或者還很模糊,請繼續閱讀。

這是為什么?

上面這段程序的輸出結果如下(windows 8.1 + visual studio 2012 update3下運行):

復制代碼 代碼如下:

// Print the memory size of the struct
cout<< sizeof(a)<<endl; // 8bytes
cout<< sizeof(b)<<endl; // 12bytes
cout<< sizeof(c)<<endl; // 8bytes

很奇怪么?定義的三個結構體,只是換了一下結構體中定義的成員的先后順序,怎么最終得到的結構體所占用的內存大小卻不一樣呢?很詭異么?好了,這就是我這里要總結的內存對齊概念了。

內存對齊

內存對齊的問題主要存在于理解struct和union等復合結構在內存中的分布。許多實際的計算機系統對基本類型數據在內存中存放的位置有限制,它們會要求這些數據的首地址的值是某個數k(通常它為4或8)的倍數,這就是所謂的內存對齊。這個值k在不同的CPU平臺下,不同的編譯器下表現也有所不同,現在我們涉及的主流的編譯器是Microsoft的編譯器和GCC。

對于我們這種做上層應用的程序員來說,真的是很少考慮內存對齊這個問題的,內存對齊對于上層程序員來說,是“透明的”。內存對齊,可以說是編譯器做的工作,編譯器為程序中的每個數據塊安排在適當的內存位置上。很多時候,我們要寫出效率更高的代碼,此時我們就需要去了解這種內存對齊的概念,以及編譯器在后面到底偷偷摸摸干了點什么。特別是對于C和C++程序員來說,理解和掌握內存對齊更是重要的。

為什么要有內存對齊呢?該占用多大的內存,那就開辟對應大小的內存就好了,好比上面的結構體,兩個char類型和一個int類型,大小應該是6bytes才對啊,怎么又是8bytes,又是12bytes的啊?對于內存對齊,主要是為了提高程序的性能,數據結構,特別是棧,應該盡可能地在自然邊界上對齊。原因在于,為了訪問未對其的內存,處理器需要做兩次內存訪問;然而,對齊的內存訪問僅僅需要一次內存訪問。

在計算機中,字、雙字和四字在自然邊界上不需要在內存中對齊(對字、雙字和四字來說,自然邊界分別是偶數地址,可以被4整除的地址和可以被8整除的地址)。如果一個字或雙字操作數跨越了4字節邊界,或者一個四字操作數跨越了8字節邊界,就被認為是未對齊的,從而需要兩次總線周期來訪問內存。一個字起始地址是奇數,但卻沒有跨越字邊界,就被認為是對齊的,能夠在一個總線周期中被訪問。綜上所述,內存對齊可以用一句話來概括――數據項只能存儲在地址是數據項大小的整數倍的內存位置上。

我們再來看看一個簡答的例子:

復制代碼 代碼如下:

#include <stdio.h>
 
struct Test
{
     char a;
     int b;
     int c;
     char d;
};
 
int main()
{
     struct Test structTest;
     printf("&a=%p/n", &structTest.a);
     printf("&b=%p/n", &structTest.b);
     printf("&c=%p/n", &structTest.c);
     printf("&d=%p/n", &structTest.d);
 
     printf("sizeof(Test)=%d/n", sizeof(structTest));
     return 0;
}

輸出結果如下:

復制代碼 代碼如下:

&a=00C7FA44
&b=00C7FA48
&c=00C7FA4C
&d=00C7FA50
sizeof(Test)=16

結構體Test的成員變量b占用字節數為4bytes,所以只能存儲在4的整數倍的位置上,由于a只占用1一個字節,而a的地址00C7FA44和b的地址00C7FA48之間相差4bytes,這就說明,a其實也占用了4個字節,這樣才能保證b的起始地址是4的整數倍。這就是內存對齊。如果沒有內存對齊,我們再拿上面的代碼作為例子,則可能輸出結果如下:

復制代碼 代碼如下:

&a=ffbff5e8
&b=ffbff5e9
&c=ffbff5ed
&d=ffbff5f1
sizeof(Test)=10

可以看到,a占用了一個字節,緊接著a之后就是b;之前也說了,內存對齊是操作系統為了快速訪問內存而采用的一種策略,簡單來說,就是為了防止變量的二次訪問。操作系統在訪問內存時,每次讀取一定的長度(這個長度就是操作系統的默認對齊系數,或者是默認對齊系數的整數倍)。沒有了內存對齊,當我們讀取變量c時,第一次讀取0xffbff5e8~0xffbff5ef的內存,第二次讀取0xffbff5f0~0xffbff5f8的內存,由于變量c所占用的內存跨越了兩片地址區域,為了正確得到變量c的值,就需要讀取兩次,將兩次內存合并進行整合,這樣就降低了內存的訪問效率。

我在這里說了這么多,也挺繞口,這就是內存對齊的規則。在C++中,每個特定平臺上的編譯器都有自己的內存對齊規則,下面我們就內存對齊的規則進行總結。

內存對齊規則

每個特定平臺上的編譯器都有自己的默認“對齊系數”。我們可以通過預編譯命令#pragma pack(k),k=1,2,4,8,16來改變這個系數,其中k就是需要指定的“對齊系數”;也可以使用#pragma pack()取消自定義字節對齊方式。具體的對齊規則如下:

規則1:struct或者union的數據成員對齊規則:第一個數據成員放在offset為0的地方,對齊按照#pragma pack指定的數值和自身占用字節數中,二者比較小的那個進行對齊;比如;

復制代碼 代碼如下:

#pragma pack(4) // 指定對齊系數為4,當占用字節數大于等于4時,就按照4進行對齊
struct Test
{
     char x1;
     short x2;
     float x3;
     char x4;
};

x1占用字節數為1,1 < 4,按照對齊系數1進行對齊,所以x1放置在offset為0的位置;
x2占用字節數為2,2 < 4,按照對齊系數2進行對齊,所以x2放置在offset為2,3的位置;
x3占用字節數為4,4 = 4,按照對齊系數4進行對齊,所以x3放置在offset為4,5,6,7的位置;
x4占用字節數為1,1 < 4,按照對齊系數1進行對齊,所以x4放置在offset為8的位置;
現在已經占了9bytes的內存空間了,但是實際在visual studio 2012中實測為12bytes,為什么呢?看下一條規則。

規則2:struct或者union的整體對齊規則:在數據成員完成各自對齊以后,struct或者union本身也要進行對齊,對齊將按照#pragma pack指定的數值和struct或者union中最大數據成員長度中比較小的那個進行;

繼續使用規則1種的例子進行解釋,按照規則1的理解,struct Test已經占用了9bytes,實際為什么是12bytes呢?根據規則2,在所有成員對齊完成以后,struct或者union自身也要進行對齊;我們設定的對齊系數為4,而struct Test中占用字節數最大的是float類型的x3,由于x3占用字節數小于或等于設定的對齊系數4,所以struct或者union整體需要按照4bytes進行對齊,也就是說,struct或者union占用的字節數必須能夠被4整除,好了。struct Test已經占用了9bytes了,10bytes不能被4整除,11bytes也不能,12bytes正好;所以,struct Test最終占用的字節數為12bytes。

上述兩條規則就是內存對齊的基本規則,先局部對齊,后整體對齊。

實例分析

總結了那么多的規則,不來點實際的code,總覺的少點什么,好吧。以下就按照上述總結的內存對齊規則,來進行一些實際的代碼分析(注:測試環境Windows 8.1 + Visual Studio 2012 update 3)。

測試代碼如下,先確認測試環境:

復制代碼 代碼如下:

#include <iostream>
using namespace std;
 
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};
 
int main()
{
     cout<<"sizeof(char)"<<sizeof(char)<<endl;          // 1byte
     cout<<"sizeof(short)"<<sizeof(short)<<endl;        // 2bytes
     cout<<"sizeof(int)"<<sizeof(int)<<endl;            // 4bytes
     cout<<"sizeof(double)"<<sizeof(double)<<endl;      // 8bytes
     return 0;
}

我分別設置#pragma pack(k),k=1,2,4,8,16進行測試。

復制代碼 代碼如下:

#pragma pack(1) // 設定對齊系數為1
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};

首先使用規則1,對成員變量進行對齊:
x1 <= 1,按照1進行對齊,x1占用0;
x2 > 1,按照1進行對齊,x2占用1,2,3,4,5,6,7,8;
x3 > 1,按照1進行對齊,x3占用9,10;
x4 > 1,按照1進行對齊,x4占用11,12,13,14;
x5 > 1,按照1進行對齊,x5占用15;
最后使用規則2,對struct整體進行對齊:
x2占用內存最大,為8bytes,8bytes > 1byte,所以整體按照1進行對齊;16%1=0。
所以,在#pragma pack(1) 的情況下,struct Test占用內存為16bytes;內存占用如下圖所示:

復制代碼 代碼如下:

#pragma pack(2) // 設定對齊系數為2
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};

首先使用規則1,對成員變量進行對齊:
x1 <= 2,按照1進行對齊,x1占用0;
x2 > 2,按照2進行對齊,x2占用2,3,4,5,6,7,8,9;
x3 >= 2,按照2進行對齊,x3占用10,11;
x4 > 2,按照2進行對齊,x4占用12,13,14,15;
x5 < 2,按照1進行對齊,x5占用16;
最后使用規則2,對struct整體進行對齊:
x2占用內存最大,為8bytes,8bytes > 2byte,所以整體按照2進行對齊;17%2!=0
所以,在#pragma pack(2) 的情況下,struct Test占用內存為18bytes;內存占用如下圖所示:

復制代碼 代碼如下:

#pragma pack(4) // 設定對齊系數為4
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};

首先使用規則1,對成員變量進行對齊:
x1 <= 4,按照1進行對齊,x1占用0;
x2 > 4,按照4進行對齊,x2占用4,5,6,7,8,9,10,11;
x3 < 4,按照2進行對齊,x3占用12,13;
x4 >= 4,按照4進行對齊,x4占用16,17,18,19;
x5 < 4,按照1進行對齊,x5占用20;
最后使用規則2,對struct整體進行對齊:
x2占用內存最大,為8bytes,8bytes > 4byte,所以整體按照4進行對齊;21%4!=0
所以,在#pragma pack(4) 的情況下,struct Test占用內存為24bytes;內存占用如下圖所示:

復制代碼 代碼如下:

#pragma pack(8) // 設定對齊系數為8
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};

首先使用規則1,對成員變量進行對齊:
x1 <= 8,按照1進行對齊,x1占用0;
x2 >= 8,按照8進行對齊,x2占用8,9,10,11,12,13,14,15;
x3 < 8,按照2進行對齊,x3占用16,17;
x4 <= 8,按照4進行對齊,x4占用20,21,22,23;
x5 < 8,按照1進行對齊,x5占用24;
最后使用規則2,對struct整體進行對齊:
x2占用內存最大,為8bytes,8bytes >= 8byte,所以整體按照8進行對齊;25%8!=0
所以,在#pragma pack(8) 的情況下,struct Test占用內存為32bytes;內存占用如下圖所示:

復制代碼 代碼如下:

#pragma pack(16) // 設定對齊系數為16
struct Test
{
     char x1;
     double x2;
     short x3;
     float x4;
     char x5;
};

首先使用規則1,對成員變量進行對齊:
x1 < 16,按照1進行對齊,x1占用0;
x2 < 16,按照8進行對齊,x2占用8,9,10,11,12,13,14,15;
x3 < 16,按照2進行對齊,x3占用16,17;
x4 < 16,按照4進行對齊,x4占用20,21,22,23;
x5 < 16,按照1進行對齊,x5占用24;
最后使用規則2,對struct整體進行對齊:
x2占用內存最大,為8bytes,16bytes >= 8byte,所以整體按照8進行對齊;25%8!=0
所以,在#pragma pack(16) 的情況下,struct Test占用內存為32bytes;內存占用如下圖所示:

總結

經過上面的實例分析,我對內存對齊有了全面的認識和了解?,F在再回過來看看文章開頭的那段代碼,問題就迎刃而解了,同時經過這段代碼,讓我們認識到定義struct或者union時,也是有講解的。在以后的編碼生涯時,是不是又要多考慮一些呢?糾結~

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久久久久国产免费| 伊人亚洲福利一区二区三区| 中文字幕精品www乱入免费视频| 亚洲va欧美va在线观看| 色偷偷偷综合中文字幕;dd| 麻豆国产精品va在线观看不卡| 尤物九九久久国产精品的分类| 黑人巨大精品欧美一区二区三区| 色综合久久中文字幕综合网小说| 日韩国产精品视频| 正在播放亚洲1区| 日韩视频亚洲视频| 最好看的2019的中文字幕视频| 精品久久久久久久久久久久久| 成人在线视频网站| 欧美多人乱p欧美4p久久| 国产精品69精品一区二区三区| 色狠狠av一区二区三区香蕉蜜桃| 欧美韩国理论所午夜片917电影| 国产精品69久久| 欧美精品激情在线| 8x海外华人永久免费日韩内陆视频| 美女黄色丝袜一区| 国产精品第2页| 亚洲精品一区二区在线| 国产网站欧美日韩免费精品在线观看| 欧美亚洲视频在线观看| 在线视频欧美日韩精品| 久久精品精品电影网| 亚洲人成网站色ww在线| 日韩国产高清视频在线| 91wwwcom在线观看| 欧美体内谢she精2性欧美| 91精品国产91久久久久久最新| 国产精品露脸av在线| 日韩成人激情影院| 亚洲天堂av综合网| 欧美一区二粉嫩精品国产一线天| 精品一区电影国产| 成人444kkkk在线观看| 久青草国产97香蕉在线视频| 亚洲综合第一页| 日韩电影中文字幕av| 神马国产精品影院av| 亚洲在线视频福利| 久久久久久成人| 日韩中文字幕在线免费观看| 中国人与牲禽动交精品| 91po在线观看91精品国产性色| 一本色道久久综合狠狠躁篇怎么玩| 欧美日韩国产成人在线| 日韩在线观看免费| 欧美自拍大量在线观看| 欧美黑人xxxⅹ高潮交| 91色琪琪电影亚洲精品久久| 色综合久久久久久中文网| 69国产精品成人在线播放| 国产成人精品综合| 伊人久久久久久久久久| 日韩av中文字幕在线| 国产精品免费在线免费| 亚洲人成电影网站色xx| 国产欧美亚洲精品| 国产91精品久久久| 精品国产欧美成人夜夜嗨| 日韩在线观看网址| 日韩精品在线观看视频| 中文字幕成人精品久久不卡| 一区二区在线视频播放| 成人激情视频在线| 777国产偷窥盗摄精品视频| 日韩一级裸体免费视频| 亚洲欧美日韩视频一区| 久久99热这里只有精品国产| 91精品国产九九九久久久亚洲| 亚洲精品在线不卡| 国产精品福利小视频| 亚洲视频视频在线| 中文字幕av一区二区| 欧美成人免费观看| 亚洲精品美女网站| 国产精品久久久久久久7电影| 亚洲人免费视频| 九九热99久久久国产盗摄| 成人亚洲欧美一区二区三区| 91精品国产91久久久| 国产精品美女视频网站| 成人免费福利视频| 懂色aⅴ精品一区二区三区蜜月| 久久精品国产久精国产一老狼| 91国产视频在线| 97国产suv精品一区二区62| 欧美日韩ab片| 久久精品中文字幕| 亚洲专区在线视频| 中文字幕亚洲欧美日韩高清| 韩国福利视频一区| 日韩精品在线视频| 亚洲国产成人爱av在线播放| 久久久久久久久网站| 欧美刺激性大交免费视频| 久久精品视频99| 国产欧美精品va在线观看| 国产精品com| 亚洲天堂第二页| 久久久久女教师免费一区| 欧美日产国产成人免费图片| 久久精品国产欧美激情| yellow中文字幕久久| 亚洲第一网站免费视频| 亚洲视频axxx| 欧美激情视频在线免费观看 欧美视频免费一| 色噜噜久久综合伊人一本| 亚洲男人天堂久| 欧美日韩亚洲视频一区| 成人免费在线视频网站| 日本精品久久中文字幕佐佐木| 国产精品久久二区| 久久久www成人免费精品| 亚洲精品国产精品乱码不99按摩| 18一19gay欧美视频网站| 国产福利精品av综合导导航| 日韩欧美中文第一页| 懂色av一区二区三区| 欧美激情一级欧美精品| 国产成人高清激情视频在线观看| 亚洲欧美中文在线视频| 欧美大胆a视频| 国产精品免费网站| 国产亚洲欧美视频| 欧美性xxxx极品高清hd直播| 国产精品美女www| 久久影院资源网| 精品久久久国产| 亚洲午夜久久久久久久| 国产精品劲爆视频| 亚洲激情国产精品| 91免费的视频在线播放| 精品亚洲va在线va天堂资源站| 欧美精品免费在线| 最近中文字幕2019免费| 中文字幕无线精品亚洲乱码一区| 黑人巨大精品欧美一区二区一视频| 97福利一区二区| 久久久999成人| 日韩在线观看免费高清完整版| 俺去亚洲欧洲欧美日韩| 日本高清不卡在线| 亚洲国产精品人久久电影| 亚洲国产精品va在线看黑人| 色哟哟网站入口亚洲精品| 久久亚洲春色中文字幕| 成人性生交大片免费观看嘿嘿视频| 91九色视频在线| 亚洲综合在线播放| 欧美国产日韩一区二区| 亚洲欧洲视频在线| 久久91亚洲人成电影网站| 亚洲国内精品在线| 亚洲女人被黑人巨大进入| 久久久久久久久久久成人| 日韩av免费网站| 国产成人精品免费久久久久| 亚洲一区二区三区xxx视频|