【问题标题】:Can two objects of different types with different alingment requirements have the same object representation?具有不同对齐要求的两个不同类型的对象可以具有相同的对象表示吗?
【发布时间】:2014-10-05 07:38:53
【问题描述】:

给出了对象表示的以下定义(3.9/4):

T类型对象的对象表示 N的序列 类型 T 的对象占用的 unsigned char 对象,其中 N 等于 大小(T)。

但是 3.9.1/1 说:

charsigned charunsigned char 占用相同数量的 存储并具有相同的对齐要求 (3.11);也就是说,他们 具有相同的对象表示。

看起来对象表示取决于对齐要求。但是我引用的定义中没有提到它。那就是两个相同大小的对象可能有不同的对象表示,是吗?

基本上,我问的是以下问题: 假设我们有两个大小相同的对象,其中一个对象的对齐方式与另一个不同。例如:

struct A
{
    char a;
    char b;
    char c;
    char d;
};

A a; //Object 1. alignof(a) = 1
int b; //Object 2. alignof(b) = 4

这些对象是否具有相同的对象表示?

【问题讨论】:

  • 如果您有任何代码可以作为实际的激励示例,那将有所帮助。
  • @JohnZwinck 不幸的是,我没有。这纯粹是理论上的问题。
  • 我很确定在另一种类型中手动指定对齐方式的类型会计算在内。
  • 您是在问是否可以将 2 个相同大小的内存块表示为 2 个不同的类类型?或者您是在问同一个类是否可以通过不同的对齐方式以不同的方式表示?
  • @JohnZwinck 我提供了一个小例子来说明我在问什么。

标签: c++ language-lawyer memory-alignment


【解决方案1】:

关于对齐的工作原理有些混乱。

您说得对,对象表示取决于对齐要求:

对象表示是 unsigned char 类型的 sizeof(T) 对象的序列

同时

对象的值表示是保存其类型 T 的值的位集合

如果你以下面的类型为例:

struct S {
    char c;  // 1 byte value
             // 3 bytes padding
    float f; // 4 bytes value
    bool operator==(const S& arg) const { // value-based equality
        return c == arg.c && f == arg.f;
    }
};
assert(sizeof(S) == 8); // object representation

对象表示大小占 8 个字节,而值表示大小仅占 5 个字节,这决定了对象相对于另一个对象的值。存在导致差异的对齐要求,它们引入了一些填充。

在您的示例中,对象表示和值表示的类型具有相同的大小,加上对象表示的大小等于值表示的大小。

struct A
{
    char a;
    char b;
    char c;
    char d;
};

A a; //Object 1. Object representation size = 4, value representation size = 4, alignof(a) = 1
int b; //Object 2. Object representation size = 4, value representation size = 4, alignof(b) = 4

这里的不同之处在于存储和访问对象所需的对齐方式。

在某些处理器上,访问非 4 字节对齐地址上的 4 字节整数会产生致命错误。您的 sn-p 中使用的 alignof 关键字正是这样说的:您需要在 1 字节对齐的地址(即任何地方)上分配 A 类型的对象,因为您要访问单个字节的子对象作为最大单次读取,它们可以安全地在任何地方访问,无论如何您需要在 4 字节对齐的地址上分配一个整数,以便我可以安全地访问它

§3.11/p1

对象类型有对齐要求(3.9.1、3.9.2) 对该类型对象的地址限制 分配。对齐是实现定义的整数值 表示连续地址之间的字节数 可以分配给定的对象

这意味着:您应该在 4 字节对齐的地址上分配一个整数,但是从缓冲区开头到对象开头所需的字节不会成为对象的一部分

|0x07|0x08|0x09|0x0A|0x0B|..
     > I can allocate an integer here, on a 4-bytes-aligned address
> Here the buffer starts
      |------------------|
        object rep size == value rep size

请注意,如果不满足对齐要求会发生什么取决于系统,即在 x86 上性能下降,在 SSE 上可能会崩溃,在 GPU 内存空间上则无法恢复你的程序。该标准仅规定了对齐分配请求应发生的情况:

如果在特定上下文中请求特定的扩展对齐 没有实现支持,程序格式不正确。 此外,对动态存储的运行时分配请求 所请求的对齐不能被兑现,应被视为 分配失败。

【讨论】:

  • 感谢详尽的回答。
【解决方案2】:

在单字节字符的情况下,对齐是无关紧要的(一个字节边界),所以引用的例子是正确的。但是,在更高级的情况下,情况并非如此。

作为一般经验法则,n 字节对象需要存储在 n 字节边界上。在处理器级别,尝试对未正确对齐的对象执行操作会导致异常。

所以(没有优化):

PTR64     *a;
INT32      b;
INT16      c;
BYTE       d;

将是 8+4+2+1 = 15 个字节。 然而:

BYTE       a;
INT16      b;  // preceded with (1) bytes to 16 bit boundary
INT32      c;  // Preceded with (2) bytes to 32 bit boundary
PTR64     *d;  // preceded with (4) bytes to 64 bit boundary

b 1+(1)+2+(2)+4+(4)+8 = 21 个字节。

【讨论】:

  • “在处理器级别,尝试对不正确对齐的对象进行操作会导致异常。” - 不适用于当今使用的一些最常见的处理器,包括 x86 和 x86-64。它可能导致错误,但不是必须的。
  • 因此“作为一般经验法则”。例外情况也适用于简单的 8 位嵌入式系统,除了在不对齐时添加时钟周期外,几乎没有或没有 16 位边界的概念。
【解决方案3】:

当然可以。
想想 intchar[4] 完全相同的大小,不同的对象表示。

您的语言中还有 reinterper_castunion。这些可以帮助您将相同的字节视为不同的对象类型。

因此,使用您的示例,您可以有一个 int 的联合,并且您的结构在相同的实际字节上:

struct A
{
    char a;
    char b;
    char c;
    char d;
};

union MyUnion
{
   A a;
   int b;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 2015-06-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多