Re: [問題] sizeof(size_t) 之倍數
原則上同學回答的是正確的。
關於 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
討論串 (同標題文章)
完整討論串 (本文為第 3 之 3 篇):