請說出以下2種方式,哪一種更好,爲何? ios
方式一: 程序員
void foo(int a, float b, char* ch, double d, float f);方式二:
struct A { int a; float b; char ch[5]; double d; float f; }; void foo(A* pa);
咋一看,不知道這題想要考什麼,無從下手。其實該題是檢查考生對於內存對齊的理解。下面咱們先看看關於內存的一些知識。 面試
什麼是字節對齊,爲何要對齊? 編程
現代計算機中內存空間都是按照byte劃分的,從理論上講彷佛對任何類型的變量的訪問能夠從任何地址開始,但實際狀況是在訪問特定類型變量的時候常常在特定的內存地址訪問,這就須要各類類型數據按照必定的規則在空間上排列,而不是順序的一個接一個的排放,這就是對齊。 架構
對齊的做用和緣由:各個硬件平臺對存儲空間的處理上有很大的不一樣。一些平臺對某些特定類型的數據只能從某些特定地址開始存取。好比有些架構的CPU在訪問一個沒有進行對齊的變量的時候會發生錯誤,那麼在這種架構下編程必須保證字節對齊.其餘平臺可能沒有這種狀況,可是最多見的是若是不按照適合其平臺要求對數據存放進行對齊,會在存取效率上帶來損失。好比有些平臺每次讀都是從偶地址開始,若是一個int型(假設爲32位系統)若是存放在偶地址開始的地方,那麼一個讀週期就能夠讀出這32bit,而若是存放在奇地址開始的地方,就須要2個讀週期,並對兩次讀出的結果的高低字節進行拼湊才能獲得該32bit數據。顯然在讀取效率上降低不少。 大數據
對齊規則 spa
每一個特定平臺上的編譯器都有本身的默認「對齊係數」(也叫對齊模數,32位gcc 4.7上默認爲8,32位VS2010上默認爲8)。程序員能夠經過預編譯命令#pragma pack(n),n=1,2,4,8,16來改變這一系數,其中的n就是你要指定的「對齊係數」。 code
規則: 對象
一、數據成員對齊規則:結構(struct)(或聯合(union))的數據成員,第一個數據成員放在offset爲0的地方,之後每一個數據成員的對齊按照#pragma pack指定的數值和這個數據成員 內存
自身長度中,比較小的那個進行。
二、結構(或聯合)的總體對齊規則:在數據成員完成各自對齊以後,結構(或聯合)自己也要進行對齊,對齊將按照#pragma pack指定的數值和結構(或聯合)最大數據成員長度中,比較小的那個進行。
三、結合一、2可推斷:當#pragma pack的n值等於或超過全部數據成員長度的時候,這個n值的大小將不產生任何效果。
筆者總結:當用sizeof求結構的大小時,第一個數據大小始終爲該數據的sizeof大小(即偏移位始終爲0),與對齊係數無關。下一個數據的仍爲其sizeof的大小,但偏移位爲min(數據的sizeof大小,對齊係數)。最後對結構自己進行對齊,即結構的sizeof大小爲min(結構中最大數據成員長度,對齊係數)的整數倍。注意該規則不適用於有設置位域的結構。
實例1(32位GCC 4.7):
#include <iostream> #include <cstddef> using namespace std; //#pragma pack(push, 4) struct A { int a; float b; char c; double d; int *pa; char *pc; }; //#pragma pack(pop) int main() { cout << offsetof(A, a) << endl; cout << offsetof(A, b) << endl; cout << offsetof(A, c) << endl; cout << offsetof(A, d) << endl; cout << offsetof(A, pa) << endl; cout << offsetof(A, pc) << endl; cout << sizeof(A) << endl; }輸出結果:
0 4 8 16 24 28 32
分析結果:這裏默認對齊係數爲8,讀者能夠根據上面的規則推算一下,sizeof(A) = 4(a) + 4(b) + 1(c) + 7(偏移位) + 8(d) + 4(pa) + 4(pc) = 32。注意,這裏別忘告終構自己的對齊,上面各個數據成員偏移後的大小恰好是對齊係數8的整數倍,就不須要再進行偏移了。若是將上面的註釋去掉,即將默認對齊係數改成4,想一想sizeof(A)的大小?sizeof(A) = 4(a) + 4(b) + 1(c) + 4(偏移位) + 8(d) + 4(pa) + 4(pc) = 28。
這裏的offsetof是查看數據成員在結構中的偏移位,定義在cstddef文件中。當你#pragma pack(push,n)改變默認對象係數時,n只能取一、二、四、八、16,將你想要進行內存對齊的結構放入#pragma pack(push, n) ... #pragma pack(pop)之間,這樣能夠防止將其餘的結構也進行內存對齊。
如今回到上面的面試題,方式一調用不存在內存對齊的問題,傳入的數據大小就是其原本的數據大小,而方式二就不同了,因爲內存對齊的緣由,將致使傳入的結構大小爲32(32位GCC 4.7)個字節,sizeof(A) = 4(a) + 4(b) + 5(ch) + 3(偏移) + 8(d) + 4 + 4(結構自己對齊偏移) = 32。顯然方式一的效果比較高。