打包或不打包仅包含数组的结构

时间:2015-08-17 16:11:54

标签: c++ c arrays gcc buffer

实验: 让我们在c / c ++中声明一个SHA-512摘要容器(使用GCC):

#define DIGEST_LENGTH 512
struct Digest {
  uint32_t bits[DIGEST_LENGTH / 8 / sizeof(uint32_t)];
} __attribute__((packed));

让我们不要争论uint32_t数组而不是char数组的选择。就这样吧。

然后我们可以按照下面显示的方式从工作缓冲区读取摘要:

Digest digest;
......
memcpy(&digest, buffer, sizeof(Digest));

同样,我们可以将摘要写入工作缓冲区:

memcpy(buffer, &digest, sizeof(Digest)); //Assuming sufficient buffer size

我的问题:

一个。 fillof属性是否必要且sizeof(Digest)的充分条件总是返回正确的大小(= 512位或64字节)?

B中。当我们保留打包属性时,digest-> bits [i]是否对所有体系结构都是安全的操作?

℃。我们可以在保持容器不透明的同时简化表示吗?

d。如果我们保留代表,是否需要支付运行时罚款?

我知道有关于packed属性的其他问题,但我的问题是专门针对包含单个基本类型数组的结构。

2 个答案:

答案 0 :(得分:2)

  

一个。包装属性是必要的和充分条件   sizeof(Digest)总是返回正确的大小(= 512位或64位)   字节)?

这就足够了。

  

B中。 digest-> bits [i]在我们所有架构上都是安全的操作   保持打包属性?

我认为您不理解__attribute__((packed))。以下是实际的内容。

  

当在结构声明中使用packed时,它将压缩其   这样的字段,使得sizeof(结构)== sizeof(first_member)+    ... + sizeof(last_member)。

以下是上述法规资源的网址Effects of __attribute__((packed)) on nested array of structures?

修改

当然是安全的。打包定义内存中的布局,但不要担心,因为即使数据未对齐,编译器也会处理访问特定数据类型。

  

℃。我们可以在保持容器的同时简化表示   不透明?

是的,您可以定义一个简单的缓冲区uint32_t bits[LENGTH];,它将以同样的方式为您工作。

  

d。如果我们保留代表,是否需要支付运行时罚款?

一般来说是的。打包强制编译器不在成员之间的数据结构中执行填充。数据结构中的填充使物理对象更大,但是对单个字段的访问更快,因为它只是读取操作,例如不需要读取,掩码和旋转。

请在下面查看这个非常简单的程序,显示包装对结构尺寸的影响。

#include <stdio.h>
#include <stdint.h>

#pragma pack(push, 1) 
typedef struct _aaa_t {
  uint16_t a;
  uint8_t b;
  uint8_t c;
  uint8_t d;
} aaa_t;
#pragma pack(pop)

typedef struct _bbb_t {
  uint16_t a;
  uint8_t b;
  uint8_t c;
  uint8_t d;
} bbb_t;

int main(void) {
    aaa_t a;
    bbb_t b;
    printf("%d\n", sizeof(a));
    printf("%d\n", sizeof(b));
    printf("%p\n", &(a.a));
    printf("%p\n", &(a.b));
    printf("%p\n", &(a.c));
    printf("%p\n", &(a.d));
    printf("%p\n", &(b.a));
    printf("%p\n", &(b.b));
    printf("%p\n", &(b.c));
    printf("%p\n", &(b.d));
    return 0;
}

节目输出:

5
6
0xbf9ea115
0xbf9ea117
0xbf9ea118
0xbf9ea119
0xbf9ea11a
0xbf9ea11c
0xbf9ea11d
0xbf9ea11e

<强>解释

Packed:
     ____________ _______ _______ _______ _______
    |            |       |       |       |       |
    | 0xbf9ea115 | msb_a | lsb_a | lsb_b | lsb_c |
    |____________|_______|_______|_______|_______|
    |            |       |
    | 0xbf9ea119 | lsb_d |
    |____________|_______|

Not Packed:
     ____________ _______ _______ _______ _______
    |            |       |       |       |       |
    | 0xbf9ea11a | msb_a | lsb_a | lsb_b | lsb_c |
    |____________|_______|_______|_______|_______|
    |            |       |       |
    | 0xbf9ea11e | lsb_c |  pad  |
    |____________|_______|_______|

编译器会这样做,以便生成比没有填充和内存对齐优化的代码更快地访问数据类型的代码。

您可以在此链接demo program

下运行我的代码

答案 1 :(得分:1)

结构只有一个成员,所以“打包”它是没有意义的。成员之间没有填充,因为没有其他成员。

您可能想要打包数组,但这是不必要的,因为dayOfYear是一个精确大小的类型。 (不需要存在,但对于缺少0的架构,问题无关紧要。)

因此,如果你有一些偏心的48位架构,其中每个“字”由四个可寻址的12位“字节”组成,你可能有一个编译器,其中uint32_t是三个“字节”长使用四字节对齐,但不会有uint32_t,因为int类型是36位,而不是32位,(C99§7.20.1.1,它包含在C中的引用) ++ 11):

  

typedef名称uint32_t指定一个有符号整数类型,其宽度为N,无填充位,以及二进制补码表示。