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

C說話、C++內存對齊成績詳解

編輯:關於C++

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


這也能夠?


#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;內存占用以下圖所示:

總結

經由下面的實例剖析,我對內存對齊有了周全的熟悉和懂得。如今再回過去看看文章開首的那段代碼,成績就水到渠成了,同時經由這段代碼,讓我們熟悉到界說struct或許union時,也是有講授的。在今後的編碼生活時,是否是又要多斟酌一些呢?糾結~

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