【问题标题】:Can technically objects occupy non-contiguous bytes of storage?从技术上讲,对象可以占用不连续的存储字节吗?
【发布时间】:2017-02-09 13:19:52
【问题描述】:

在回答this question 时,我被要求提供标准报价。在 C++14 草案中我震惊地发现:

§ 3.9 类型 [basic.types]

  1. T 类型对象的对象表示是 T 类型对象占用的 N 个 unsigned char 对象的序列,其中 N 等于 sizeof(T)

嗯.. 它并没有说“无符号字符对象”在内存中必须是连续的。也许它是由“序列”暗示的。然后我发现了“连续存储字节”的具体提及,但是......

§ 1.8 C++ 对象模型 [intro.object]

  1. [...] 普通可复制或标准布局类型 (3.9) 的对象应占用连续的存储字节。

什么?只需要普通可复制和标准布局类型来占用连续字节的存储空间吗?其余类型在它们占用的存储空间中可以有“洞”吗?我搜索了标准的其余部分,但找不到与“连续存储”的任何其他相关性。当然,我对标准不是很熟悉。

如果这是真的(对我来说,这将是对标准最大的震惊)sizeof 和指针算术如何实现? (曾经)真的有任何架构/编译器使用(使用的)非连续字节存储类型吗?

我真的希望我误解和/或遗漏了什么。


edit:我认为这可能与填充有关,但这种解释只有在普通可复制或标准布局类型不能有填充时才有意义。然后你说这些类型占用了连续的字节存储空间,而对于其他可以填充的类型,你不会这么说。但显然情况并非如此,因为任何结构类型都可以有填充。

【问题讨论】:

  • @JoachimPileborg 这也是我所知道的。但是普通可复制和标准布局类型都有填充。那么为什么说只有那些类型才需要“连续存储”呢?
  • 对象中的孔可能是对齐要求的产物,例如当您有struct { char a; int b;} 时,布局可能是 char(1 字节)、hole(3 字节)、int(4 字节)。
  • @OlafDietsche,是的,但这适用于标准布局和可简单复制的其他结构类型。那么,为什么它将标准布局和可简单复制的布局单独列为需要连续内存的布局呢?

标签: c++ layout language-lawyer


【解决方案1】:

具有虚拟基类的类可能不在内存的连续字节中(因为虚拟基类和该类的其余部分之间的某些字节可能被另一个派生自虚拟基类的类占用。

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  |
-------

按内存顺序递增。由整数“a”和“c”组成的对象“C”不占用连续的内存位置。

【讨论】:

    【解决方案2】:

    是的。不仅对象(而不是类型)在其占用的存储空间中可以有“空洞”,它们还可能占用 char 对象 ch1、ch2,它们属于不同的连续字节序列,其地址可能有也可能没有(即两者都不是&ch1 = &ch2) 被订购。 请参阅 C++14 标准的 §5.9 (3) 和 §1.7 (1)。

    可观察到的“洞”不是填充的实例。下面,对象“分离”的对象表示(包括填充)被映射到可用 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
    

    请注意,报告的对象大小比假设布局连续时预期的要小。

    不过,指针算术绝对没有问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-23
      • 1970-01-01
      • 2022-11-09
      • 2015-01-12
      • 2014-02-22
      • 2011-05-27
      相关资源
      最近更新 更多