作者ric2k1 (Ric)
看板EE_DSnP
标题Re: [问题] sizeof(size_t) 之倍数
时间Sat Nov 19 02:30:54 2011
原则上同学回答的是正确的。
关於 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
1F:推 vincere:教授辛苦了!!!非常谢谢教授 终於把所有东西给串起来了 11/19 07:39
2F:推 nfprzkuma:推老师用心 解释得很详尽 真是辛苦了 11/19 22:31
3F:推 wmin0:推一个 11/19 23:58
4F:推 kickpp:推!! 11/20 00:22
5F:推 dream1203:推!! 11/23 16:40