Re: [問題] sizeof(size_t) 之倍數

看板EE_DSnP作者 (Ric)時間12年前 (2011/11/19 02:30), 編輯推噓5(500)
留言5則, 5人參與, 最新討論串3/3 (看更多)
原則上同學回答的是正確的。 關於 memory address alignment 的問題,我用下面這個例子來說明: ============================================================= #include <iostream> using namespace std; void *dummy = new int; // sizeof(A) = 34 --> 36 Bytes class A { int _a[8]; char _b[2]; public: ~A() {} void* operator new(size_t t) { cout << "A::new " << t << " Bytes" << endl; return dummy; } void* operator new[](size_t t) { cout << "A::new[] " << t << " Bytes" << endl; return dummy; } }; // sizeof(B) = 34 Bytes class B { char _c[34]; public: ~B() {} void* operator new(size_t t) { cout << "B::new " << t << " Bytes" << endl; return dummy; } void* operator new[](size_t t) { cout << "B::new[] " << t << " Bytes" << endl; return dummy; } }; int main() { cout << "sizeof(A) = " << sizeof(A) << endl; cout << "sizeof(B) = " << sizeof(B) << endl; A* p = new A[1]; A* q = new A[10]; B* r = new B[1]; B* s = new B[10]; } =============================================================== 執行結果 (64-bit) 是: sizeof(A) = 36 sizeof(B) = 34 A::new[] 44 Bytes A::new[] 368 Bytes B::new[] 42 Bytes B::new[] 348 Bytes =============================================================== 所以可以得到以下的結論: 1. sizeof(A) 由 34 Bytes 被 promote 成 36 Bytes 因為 class A 的 data member 包含了 int, which is 4-Byte, 所以要變成 36 Bytes 這樣下一個 A 的 object 才能保證它的記憶體位置是從 4 的倍數開始的。 例如說, A* f1 = f0 + 1; // f0 is a A* ==> f1 = f0 + sizeof(A) 2. 但是 class B 就不用 promote, 因為它包含的是 char 而已,所以 class B 的 object 可以從任何一個記憶體位置開始。 3. sizeof(A[1]) = 36 + 8 = 44 不是 8 的倍數!! 但是由於在 64-bit machine 裏頭所有的 ptr address 都要是 8 的倍數, 所以雖然系統在分配給 A[1] 的是 44 Bytes, 但是接下來可以開始使用的記憶體則至少要從 48 Bytes 以後開始。 換句話說,在作業四的 MemBlock::getMem() 雖然傳進來的是 44 Bytes, 但是你的 MemBlock::_ptr 要加上 48 才行, 這樣才能 guarantee 下次再 new 時記憶體位置是從 8 的倍數開始。 4. sizeof(B[1]) = 34 + 8 = 42 sizeof(B[10]) = 34 * 10 + 8 = 348 原因如上所述,因為 char[] 可以從任何地方開始。 但是如果要管理這樣的 class, getMem() 之後 _ptr 還是得 promote 成 8 的倍數, 這樣才能保證下次拿到的 ptr addr 是 8 的倍數。 5. 不過為什麼 64-bit machine 底下所以的 ptr addr 一定要是 8 的倍數呢? 而 32-bit .................................... 4 的倍數呢? 因為在 64/32-bit machine 一個 "word" 就是 64/32 bits ==> 8/4 Bytes, 也就是一次讀取一筆資料的大小, 為了效率起見,會讓 ptr 所指到的 memory 都是 word-aligned 的。 ===================================================================== 寫到這邊我想應該有人會把: pointer address (就是 pointer 變數裡面存的值) 以及 存 pointer 的 address 搞混。 以 "int *p" 來說,前者就是 value of p, 後者就是 address of p. 可以看下面的例子,希望不會越看越混亂 XD... ===================================================================== #include <iostream> using namespace std; class A { int _a[8]; char _b[2]; }; class B { char _c[34]; }; int main() { int *pp = new int(10); cout << "int * " << &pp << endl; char c = 'a'; cout << "char c 0x" << hex << reinterpret_cast<size_t>(&c) << endl; int a = 10; cout << "int a " << &a << endl; int *p = new int(10); cout << "int *p = new " << &p << endl; char c2 = 'b'; cout << "char c2 0x" << hex << reinterpret_cast<size_t>(&c2) << endl; int *q = p; cout << "int *q " << &q << endl; int j = 20; cout << "int j " << &j << endl; char c3 = 'c'; cout << "char c3 " << hex << reinterpret_cast<size_t>(&c3) << endl; char c4 = 'd'; cout << "char c4 " << hex << reinterpret_cast<size_t>(&c4) << endl; int k = 30; cout << "int k " << &k << endl; A aa[3]; cout << "A[2] " << &aa[2] << endl; cout << "A[1] " << &aa[1] << endl; cout << "A[0] " << &aa[0] << endl; B bb[3]; cout << "B[2] " << &bb[2] << endl; cout << "B[1] " << &bb[1] << endl; cout << "B[0] " << &bb[0] << endl; char c5 = 'e'; cout << "char c5 " << hex << reinterpret_cast<size_t>(&c5) << endl; } ====================================================================== 輸出結果 (64-bit): int * 0x7fffe1523dd8 char c 0x7fffe1523dd7 // sizeof(char) = 1 int a 0x7fffe1523dd0 // int 需要 4-Byte aligned int *p = new 0x7fffe1523dc8 // pointer 需要 8-Byte aligned char c2 0x7fffe1523dc7 int *q 0x7fffe1523db8 int j 0x7fffe1523db4 char c3 7fffe1523db3 char c4 7fffe1523db2 int k 0x7fffe1523dac A[2] 0x7fffe1523d18 A[1] 0x7fffe1523cf4 // 差 36 Bytes A[0] 0x7fffe1523cd0 B[2] 0x7fffe1523d84 B[1] 0x7fffe1523d62 // 差 34 Bytes B[0] 0x7fffe1523d40 char c5 7fffe1523dab ※ 引述《kickpp (踢屁屁)》之銘言: : 看了這篇我也想了一個小時... : 但我自己的理解是這樣... : 非常沒有把握 說不定會誤導大家XDDD : 還是請老師出面說明比較好... : :因為想了一個晚上 : :雖然有聽老師講過、爬過文 : :但感覺還是沒有完全地了解 : :不太敢隨便下手 : :所以想要請教大家一下 : :我知道要取sizeof(size_t)之倍數記憶體的原因 : :是為了做到platform dependent : :因為系統在new的時後 : :會對齊size_t為倍數的位置 : 之所以一次給size_t的倍數大小記憶體 : 應該是因為作業系統匯流排一次傳輸的bits數就是這麼大 : 32位元一次讀32 bits(4 bytes) 64位元一次讀64 bits(8 bytes) : :但在作業中 : :我們先自己跟系統要一塊memory : :來後再根據new多少再來分配記憶體 : :但這裡我不是很清楚的是 : :為何對自己要到的記憶體 : :不能依照真正object大小來要 : :而是也要跟系統一樣要sizeof(size_t)為倍數的記憶體大小呢? : :舉例說: : :一開始可能將0x00000000~0x00000007位置的記憶體分配出去 : :然後下次再從0x00000008開始開始 : :原因是因為 : :就算是我們自己已經要來的MemBlock, : :也無法aceess/或是指到非sizeof(size_t)倍數的記憶體? : 我認為pointer應該是bytewise的耶 : 所以可以acess到非size_t倍數大小位址的記憶體 : 以64-bit系統來說 : 就只是把包含你要acess的位址部分的memory一次讀出來 : 例如0x00000006就是讀出0x00000000~0x00000007然後acess第7個byte之後的東西 : :還是說只是單純要模仿機器每次都切齊sizeof(size_t)倍數的記憶體位置呢? : 所以我認為我們是在模仿機器每次給記憶體的方式(4 byte倍數 or 8 byte倍數) : 目的是為了讓我們在自己管理的memory情況下 : pointer也能像平常一般下使用不出錯 : 譬如說 : 在64-bit系統下 : 假設今天有一個class A實際大小是39 bytes : 如果我們不模仿系統存記憶體會以倍數存且留空位的話 : 今天假設宣告A* ptr = new A []; : 若計算兩個物件A占用的記憶體大小 : 系統總共用了80 bytes存放 而我們用了78 bytes存放(依此類推...) : 則 : 如果要以*(ptr + 1)來acess ptr[1] 後者就會出錯! : :或是另有其它隱情? : :不好意思小的觀念不清 : :懇請解惑 : :感激不盡 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 114.36.48.125

11/19 07:39, , 1F
教授辛苦了!!!非常謝謝教授 終於把所有東西給串起來了
11/19 07:39, 1F

11/19 22:31, , 2F
推老師用心 解釋得很詳盡 真是辛苦了
11/19 22:31, 2F

11/19 23:58, , 3F
推一個
11/19 23:58, 3F

11/20 00:22, , 4F
推!!
11/20 00:22, 4F

11/23 16:40, , 5F
推!!
11/23 16:40, 5F
文章代碼(AID): #1EngHX6W (EE_DSnP)
文章代碼(AID): #1EngHX6W (EE_DSnP)