利用其他浪费的填充空间

时间:2011-07-26 20:27:27

标签: c++ optimization memory padding nested

以下结构X有3个字节的有效负载和1个字节的填充:

struct X
{
    short a;
    char b;
};

memory layout: aab.

以下结构Y有4个字节的有效负载和2个字节的填充:

struct Y
{
    X x;
    char c;
};

memory layout: aab.c.

有没有办法让X嵌套在Y内并拥有sizeof(X) == 4 && sizeof(Y) == 4

memory layout: aabc

理想情况下,我希望对所有类型X进行此类空间优化(将X视为模板参数)。

7 个答案:

答案 0 :(得分:1)

这是一个编译器设置。在某些情况下,填充是内存访问正常工作所必需的。在某些架构中,这是效率问题,在其他架构中,如果没有正确对齐,程序将崩溃。在这种情况下,您有16位对齐,直接在奇数地址上访问c可能会导致麻烦。

但是,您可以使用pack pragma(或编译器为其提供的任何其他选项)强制关闭对齐。

问题是为什么。如果依赖于此 - 您的程序可能无法在各种体系结构上移植且不可预测。

答案 1 :(得分:1)

如果您很幸运,并且与您的示例不同,X不是POD,那么推导可以提供帮助

struct X
{
  X(){} // not POD
  short a;
  char b;
};
struct Y : X
{
  char c;
};

Y的尺寸为4,x86_64上的钛金属ABI。

答案 2 :(得分:0)

您可以使用pragma强制对齐:

#pragma pack(push, 1)
struct X
{
    short a;
    char b;
};
#pragma pack(pop)

Microsoft的C ++编译器支持此功能。我不知道这是否是一种可移植的方式。

答案 3 :(得分:0)

gcc中,您可以使用__attribute__((packed))关键字声明您的结构,以保持特定的字节对齐或填充。

例如,您可以执行类似

的操作
typedef struct X
{
    short a;
    char b;
} __attribute__((packed)) X;

typedef struct Y
{
    X x;
    char c;
} __attribute__((packed)) Y;

现在sizeof(Y)将是4个字节...唯一的问题是,为了满足硬件平台的字节对齐要求,您需要为任何需要完成的解包支付性能损失。< / p>

答案 4 :(得分:0)

是的,但它不是标准的C / C ++,需要特定于编译器的扩展。对于MSVC,请使用pack pragma

#pragma pack(push, 1)
struct X 
{
    ...
};
#pragma pack(pop)

对于GCC,请使用packed type attribute

struct __atribute__((packed)) X
{
    ...
};

使用一些预处理器宏和__pragma token,您可以在两个编译器中使定义工作,而不需要一堆#if条件。

答案 5 :(得分:0)

由于明显的原因,XY的大小不能为4(如果可能的话,X会有多个定义,正常填充的一个和一个填充由char的{​​{1}}使用。所以Y唯一的方法是4,如果Y被更改以消除其填充。这可以通过编译器特定的编译指示或指令来完成。但是,由于这可能会导致X未对齐访问,因此会有效地限制程序的可移植性(如果进行了未对齐的访问,某些体系结构甚至会崩溃)。

相反,你能更清楚地解释一下你希望在这里实现的最终结果吗?

答案 6 :(得分:0)

问题归结为:嵌套时如何将内部结构的 sizeof 进行包装,并进行 papped 进行正确对齐分配时?答:我不能,因为该语言无法提供声明,并且所有__attribute__s和#pragmas都无济于事。

最明显的解决方法是别名整个结构:

struct X
{
    short a;
    char b;
};

struct Y
{
    short a;
    char b;
    char c;
};

但是,这样做的缺点是,每当要切片结构时,都需要进行显式转换:

X_taking_fct(*(struct X *)&Y_var);

从正面看,访问外部结构的成员很自然:

char_taking_fct(Y_var.c);

这种情况可以反过来:

struct X
{
    short a;
    char b;
};

union Y
{
    struct X x;
    struct {
        short a;
        char b;
        char c;
    } extra;
};

现在您可以执行以下操作:

X_taking_fct(Y_var.x);

以需要此为代价:

char_taking_fct(Y_var.extra.c);

输入C11匿名结构(gcc支持此结构的时间更长):

struct X
{
    short a;
    char b;
};

union Y
{
    struct X x;
    struct {
        // the dummy_ prefix is not strictly necessary, but avoids confusion
        short dummy_a;
        char dummy_b;
        char c;
    };
};

现在可以同时访问内部和外部成员了:

X_taking_fct(Y_var.x);
char_taking_fct(Y_var.c);

但是,无法避免成员的多余声明。这显然很丑陋,并存在不同步的风险。通过将内部结构的主体放入宏中,至少可以避免这种风险:

#define X_body(pfx) \
    short pfx##a; \
    char pfx##b

struct X
{
    X_body();
};

union Y
{
    struct X x;
    struct {
        X_body(dummy_);
        char c;
    };
};