开发者

making use of otherwise wasted padding space

开发者 https://www.devze.com 2023-03-22 21:56 出处:网络
The following struct X has 3 bytes of payload and 1 byte of padding: struct X { short a; char b; }; memory layout: aab.

The following struct X has 3 bytes of payload and 1 byte of padding:

struct X
{
    short a;
    char b;
};

memory layout: aab.

The following struct Y has 4 bytes of payload and 2 byte of pa开发者_运维百科dding:

struct Y
{
    X x;
    char c;
};

memory layout: aab.c.

Is there any way to keep X nested inside of Y and have sizeof(X) == 4 && sizeof(Y) == 4?

memory layout: aabc

Ideally, I would want this kind of space optimization for all types X (think X as a template parameter).


This is a compiler setting. Padding is necessary for the memory access to work correctly in some cases. In some architectures it's a matter of efficiency, in others the program will crash if not properly aligned. In this case you have 16 bits alignment, and accessing c directly on an odd address may cause troubles.

However, you can force the alignment to be turned off by using pack pragma (or whatever else option your compiler has for it).

The question is why. If you rely on that - you make your program potentially not portable and unpredictable on various architectures.


If you are lucky and, unlike in your example, X is not POD, then derivation can help

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

Y has size 4 with the itanium ABI on x86_64.


For hopefully obvious reasons the size of both X and Y can't be four (if it were possible, X would have multiple definitions, the normal padded one and one where the padding is used by the char of Y). So the only way Y could be 4 is if X were changed to eliminate its padding. This can be done by compiler specific pragmas or directives. However, since that could result in X being accessed unaligned it would effectively restrict possible portability of your program (some architectures will even crash if misaligned accesses are made).

Instead, can you explain more clearly the end result you're hoping to achieve here?


You can force alignment with a pragma:

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

This is supported by Microsoft's C++ compiler. I don't know if this is a portable way of performing this.


Yes, but it's not standard C/C++ and requires compiler-specific extensions. For MSVC, use the pack pragma:

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

For GCC, use the packed type attribute:

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

With some preprocessor macros and the __pragma token, you can make a definition work in both compilers without requiring a bunch of #if conditionals.


In gcc you can declare your structure using the __attribute__((packed)) keyword in order to maintain a certain byte-alignment or padding.

So for instance, you could do something like

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

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

Now sizeof(Y) will be 4 bytes ... the only issue is that you will pay a performance penalty for any unpacking that needs to be done in order to meet the byte-alignment requirements of your hardware platform.


the question boils down to this: how can i have the inner struct's sizeof be packed when nesting it, and padded for proper alignment when allocating it? answer: i can't, because the language provides no way to declare that, and all the __attribute__s and #pragmas don't help.

the most obvious workaround is aliasing the whole structure:

struct X
{
    short a;
    char b;
};

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

however, this has the downside that you need to make an explicit cast whenever you want to slice the structure:

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

on the upside, accessing the outer structure's members is natural:

char_taking_fct(Y_var.c);

this situation can be inverted:

struct X
{
    short a;
    char b;
};

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

now you can do this:

X_taking_fct(Y_var.x);

at the cost of needing this:

char_taking_fct(Y_var.extra.c);

enter C11 anonymous structures (gcc supported this for much longer):

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

now accessing both inner and outer members is natural:

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

however, there is no way to avoid the redundant declaration of the members. this is obviously ugly and introduces a risk of running out of sync. by putting the inner struct's body inside a macro, at least the risk can be avoided:

#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;
    };
};
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号