在回答this question时,我被要求提供标准报价。我很震惊地在C ++ 14草案中找到了:
§3.9类型[basic.types]
- T类型对象的对象表示是由T类型的对象占用的N个unsigned char对象的序列,其中N 等于sizeof(T)
醇>
嗯..它没有说“unsigned char对象”必须在内存中是连续的。也许是“序列”暗示的。然后我发现了一个特别提到的“连续存储字节”,但是......
§1.8C ++对象模型[intro.object]
- [...]平凡可复制或标准布局类型(3.9)的对象应占用连续的存储字节。
醇>
什么?只需要简单的可复制和标准布局类型来占用连续的存储字节?其余的类型可以在它们占用的存储空间中有“漏洞”吗?我搜索了标准的其余部分,但找不到与“连续存储”有任何其他相关性。当然,我并不熟悉标准。
如果这是真的(对我来说这将是对标准的最大冲击)如何与sizeof
和指针算术一起使用?是否真的有任何架构/编译器使用(使用过的)不连续的存储字节用于类型?
我真的希望我误解和/或遗失一些东西。
编辑:我认为这可能与填充有关,但只有在简单的可复制或标准布局类型无法填充时,这种解释才有意义。然后你说这些类型占用了连续的字节存储空间,对于其他可以填充的类型你不会这么说。但显然不是这种情况,因为任何结构类型都可以有填充。
答案 0 :(得分:5)
具有虚拟基类的类可能不在连续的内存字节中(因为虚拟基类和类的其余部分之间的某些字节可能被另一个也来自虚拟基类的类占用。
class A {
int a;
};
class B: virtual A {
int b;
};
class C: virtual A {
int c;
};
class D: public B, C {
int D;
}
D类对象的内存可以这样组织:
-------
| a |
-------
| b |
-------
| c |
-------
| d |
-------
增加内存顺序。对象“C”由整数“a”和“c”组成,不占用连续的存储位置。
答案 1 :(得分:0)
是。不仅对象(而不是类型)在它们占用的存储中可以有“漏洞”,它们可能占用char对象ch1,ch2,它们属于不同的连续字节序列,其地址可能是也可能不是(即, & ch1< =& ch2 nor& ch1> =& ch2)被排序。 参见C ++ 14标准的§5.9(3)和§1.7(1)。
可观察的“洞”不是填充的实例。下面,包含填充的对象'separate'的对象表示被映射到可用char对象的非连续子集:
#include <iostream>
struct A { int a; };
struct B : virtual A { int b; };
struct C : virtual A { int c; };
struct D : B,C { int d; };
int main()
{
D complete;
B contiguous;
B & separate = complete;
B * p[2] = {&separate, &contiguous};
// two possible layouts for B:
std::cout<< (int)((char*)(void*) &p[0]->a -(char*)(void*)&p[0]->b)<<" "<< sizeof(*p[0])<< "\n";
std::cout<< (int)((char*)(void*) &p[1]->a -(char*)(void*)&p[1]->b)<<" "<< sizeof(*p[1])<< "\n";
}
// sample output (Debian 8, amd64):
// 24 16
// 4 16
请注意,如果布局是连续的,则报告的对象大小小于您的预期。
然而,指针算术绝对没有错。