日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++内存对齐

發布時間:2023/12/20 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++内存对齐 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里寫目錄標題

  • 空類/靜態成員
  • 內置類型數據成員
  • 結構體數據成員
  • 虛函數
  • 繼承
  • 內存對齊的意義
  • 比較兩個結構體可以使用memcmp(void*, void*)嗎?

內存對齊的基本原則:

  • 結構(struct/class)的內置類型數據成員,第一個數據成員放在offset為0的地方,以后每個數據成員的起始位置要從自身大小的整數倍開始存儲(特別注意64位機器的指針大小為8個字節)。
  • 如果一個結構A里有結構體成員B,則結構體B要從其內部"最寬基本類型成員”的整數倍地址開始存儲(如struct a里存有struct b,b里有char, int, double等元素,那b應該從8的整數倍位置開始存儲)。
  • 結構體的總大小為結構體的有效對齊值的整數倍,結構體的有效對齊值的確定:
  • 當未明確指定時,以結構體或結構體所包含結構體成員中最長的成員長度為其有效值。
  • 當用#pragma pack(n)指定時,以n和結構體中最長的成員的長度中較小者為其值。
  • 當用__attribute__ ((packed))指定長度時,強制按照此值為結構體的有效對齊值。
  • 不管# pragma pack和__attribute__如何指定,結構體內部成員的自對齊仍然按照其自身的對齊值。
  • union以結構里面size最大元素為union的size,因為在某一時刻,union只有一個成員真正存儲于該地址
  • 空類/靜態成員

    程序 1

    class A{ };int main() {cout << sizeof(A) << endl; // 1 }

    對于一個什么都沒有的空類,實際并不是空的,因為有默認的函數,具體可以參考 (待填入網址),大小是 1,這是因為需要有一個地址,C++ 不允許兩個不同的對象有相同的地址,所以 C++ 中空的類和結構體大小都是 1

    程序 2

    class A{A(){}~A(){}void print() { printf("print()\n"); }void foo() { printf("print()\n"); }static void sprint() { printf("sprint()\n"); } };int main() {cout << sizeof(A) << endl; // 1 }

    這個類的大小仍然是1,成員函數、靜態成員函數、靜態成員變量都是不占用類的內存的,這是因為這些東西都是類的,而不是每個對象分別存儲。static變量就是存儲在全局靜態區。

    需要注意的是,子類繼承空類后,子類如果有自己的數據成員,而空基類的1個字節并不會加到子類中去

    程序 3

    class Empty {}; struct D : public Empty {int a; };

    sizeof(D)為4。

    再來看另一種情況,一個類包含一個空類對象數據成員,則空類對象的大小仍為1

    程序 4

    class Empty {}; class HaveAnInt {int x;Empty e; }

    在大多數編譯器中,你會發現 sizeof(HaveAnInt) 輸出為8。這是由于,Empty類的大小雖然為1,然而為了內存對齊,編譯器會為HaveAnInt額外加上一些字節,使得HaveAnInt被放大到足夠又可以存放一個int。

    內置類型數據成員

    程序 1

    class Data {char c;int a; };cout << sizeof(Data) << endl;

    程序 2

    class Data {char c;double a; };cout << sizeof(Data) << endl;

    顯然程序 1 輸出的結果為 8,程序 2 輸出的結果為 16 .

    程序 1 最大的數據成員是4bytes,1+4=5,補齊為4的倍數,也就是8。而程序 2 為8bytes,1+8=9,補齊為8的倍數,也就是16。

    程序 3

    class Data {char c;int a;char d; };cout << sizeof(Data) << endl;

    程序 4

    class Data {char c;char d;int a; };cout << sizeof(Data) << endl;

    程序 3 運行結果為 12,程序 4 運行結果為 8

    class中的數據成員放入內存的時候,內存拿出一個內存塊來,數據成員們排隊一個一個往里放,遇到太大的成員時,不是將其劈成兩半能放多少就放多少,而是等下一個內存塊過來。這樣的話,就可以理解為什么程序 3 和程序 4 兩段代碼輸出結果不一樣了,因為程序 3 是
    1 + (3) + 4 + 1 + (3) = 12,而程序 4 是1 + 1 + (2) + 4 = 8。括號中為補齊的bytes。

    結構體數據成員

    在默認條件下,內存對齊是以class中最大的那個基本類型為基準的,如果class中的數據成員包含其他class,則遞歸的取其中最大的基本類型來參與比較。

    程序 1

    class BigData {char array[33]; };class Data {BigData bd;int integer;double d; };cout << sizeof(BigData) << " " << sizeof(Data) << endl;

    程序 2

    class BigData {char array[33]; };class Data {BigData bd;double d; };cout << sizeof(BigData) << " " << sizeof(Data) << endl;

    程序 1 和程序 2 運行結果均為:33 48

    程序 1 和程序 2 中內存對其的基準均為8字節,BigData的大小均為33。在程序 1 中,BigData接下來是個int(4bytes),能夠放下,這時候內存塊還剩3bytes,而接下來是個double(8bytes),放不下,所以要等下一個內存快到來。因此,程序 1 的Data的size = 33 + 4 + (3) + 8 = 48,同理程序 2 應該是
    33 + (7) + 8 = 48。

    程序 3

    class A { public: double len; char str[33]; }; class B { public: A a; int b; };cout << sizeof(A) << " " << sizeof(B) << endl;

    以上代碼輸出的結果為: 48 56
    不同于程序 1 和程序 2 ,程序 3 中的class A實際會占用41字節,但會發生8字節對齊,所以大小為48字節。對于class B,成員b的起始位置已發生8字節對齊,而class B整體還會發生8字節對齊,所以最終大小為56。

    虛函數

    C++ 的類中如果有虛函數,類內就會有一個虛函數表的指針 _vptr,指向自己的虛函數表,vptr 一般都是在類的最前邊(取決于編譯器的實現)。

    class A { public:A(){}virtual ~A(){}virtual void foo(){}virtual void print() {} };

    由于只是存一個指向虛函數表的指針,所以不管有多少個虛函數,都是 4 字節大小(32位下,任何指針大小都是 4,64位下,任何指針大小都是 8),比如上面這個類 A,size 就是 4。

    需要注意的是就是,對于沒有 override 的虛函數,基類和子類中 _vptr 指向的虛函數表中,這個虛函數的地址是一樣的,也就是上邊的 foo() 函數,而對于重寫了的或者默認重寫的析構函數來說,_vptr 指向的虛函數表中,函數地址是不一樣的(當然兩個類的 _vptr 地址也是不一樣的,這是肯定的),這就能窺探到多態的實現了。

    繼承

    不同的編譯器對繼承后類的大小的計算方式不同,有的是先繼承后對齊,有的是先對齊后繼承

    class A {int i;char c1; }class B:public A {char c2; }class C:public B {char c3; }

    sizeof(C)結果是多少呢,gcc和vs給出了不同的結果,分別是8、16。

    • gcc中:C相當于把所有成員i、c1、c2、c3當作是在一個class內部,(先繼承后對齊)

    • vs中:對于A,對齊后其大小是8;對于B,c2加上對齊后的A的大小是9,對齊后就是12;對于C,c3加上對齊后的B大小是13,再對齊就是16 (先對齊后繼承)

    內存對齊的意義

  • 效率原因:經過內存對齊之后,CPU的內存訪問速度大大提升。
  • 平臺原因(移植原因):不是所有的硬件平臺都能訪問任意地址上的任意數據,某些硬件平臺只能在某些地址處取某些特定類型的數據,否則拋出硬件異常。
  • 比較兩個結構體可以使用memcmp(void*, void*)嗎?

    不可以,memcmp函數是逐個字節進行比較的,而struct存在內存對齊,內存對齊時補的字節內容是垃圾值,所以無法比較。

    總結

    以上是生活随笔為你收集整理的C++内存对齐的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。