具有不同对齐要求的两个不同类型的对象可以具有相同的对象表示吗?

时间:2014-10-05 07:38:54

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

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

  

T 类型的对象的对象表示 N的序列   由类型T的对象占用的unsigned char对象,其中N等于   的sizeof(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

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

3 个答案:

答案 0 :(得分: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字节整数会产生致命错误。您的代码段中使用的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内存空间上你将无法恢复你的程序。该标准仅规定了对齐分配请求应该发生的事情:

  

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

答案 1 :(得分:0)

在单字节字符的情况下,对齐是无关紧要的(一个字节边界),因此引用的示例成立。但是,在更高级的情况下,情况并非如此。

作为一般经验法则,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个字节。

答案 2 :(得分:0)

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

您的语言中还有 reinterper_cast union 。这些可以帮助您查看与不同对象类型相同的字节。

所以,使用你的例子,你可以在同一个实际字节上有一个int和你的struct的联合:

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

union MyUnion
{
   A a;
   int b;
}