What is the difference between "#pragma pack" and "__attribute__((aligned))"
Asked Answered
F

3

29
#pragma pack(L1_CACHE_LINE)
struct A {
  //...
};
#pragma pack()

A a;

and

struct A {
  //...
};

A a __attritube__((aligned(L1_CACHE_LINE)))

What's difference between them?

Fibrinous answered 6/1, 2013 at 5:58 Comment(2)
Primary gcc references: (1) gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html (see aligned, aligned (alignment), and packed) and (2) gcc.gnu.org/onlinedocs/gcc/Structure-Layout-Pragmas.html (ex: #pragma pack(1) to force 1-byte-alignment, followed by a struct definition which now follows this forced alignment, followed by #pragma pack() to disable forced 1-byte-alignment and return to default.Azotize
See also C++11 alignas() and alignof() operators: en.cppreference.com/w/cpp/language/alignas and en.cppreference.com/w/cpp/language/alignof, respectively.Azotize
L
17

The #pragma pack(byte-alignment) effect each member of the struct as specified by the byte-alignment input, or on their natural alignment boundary, whichever is less.

The __attribute__((aligned(byte-alignment))) affect the minimum alignment of the variable (or struct field if specified within the struct)

I believe the following are equivalent

#define L1_CACHE_LINE 2

struct A
{
    u_int32_t   a   __attribute__ ( (aligned(L1_CACHE_LINE)) );
    u_int32_t   b   __attribute__ ( (aligned(L1_CACHE_LINE)) );
    u_int16_t   c   __attribute__ ( (aligned(L1_CACHE_LINE)) );       
    u_int16_t   d   __attribute__ ( (aligned(L1_CACHE_LINE)) );      
    u_int32_t   e   __attribute__ ( (aligned(L1_CACHE_LINE)) );     
};


#pragma pack(L1_CACHE_LINE)
struct A
{
    u_int32_t   a;  
    u_int32_t   b;  
    u_int16_t   c;  
    u_int16_t   d;  
    u_int32_t   e;  
};
#pragma pack()

where is A a __attritube__((aligned(L1_CACHE_LINE))) will insure u_int32_t a inside struct A will aligned with 2 byte but will not align the other variable in the same manner.

Reference:

  1. http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=%2Fcom.ibm.vacpp6m.doc%2Fcompiler%2Fref%2Frnpgpack.htm
  2. http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/attributes-variables.html
Leitmotif answered 6/1, 2013 at 6:32 Comment(2)
In one of my tests (stackoverflow.com/questions/39407675), I actually needed to add __attribute__((packed)) to the whole struct A to ensure the layout in both versions was the same. Could you please confirm that, and update the code example? I hesitate to do so in case my test is incomplete and could lead to other counterexamples.Ferraro
Be careful with using precompiler macros like L1_CACHE_LINE within pragmas.Kelantan
F
27

#pragma pack is a Microsoft syntax that has been ported to GCC for compatibility reasons.

__attribute__((aligned)) is a GCC-specific syntax (unsupported by MSVC).

Here's a summary of the differences:

  • #pragma pack (and variants) is more concise, and represents both attributes packed and aligned in GCC syntax (see example below);
  • #pragma pack applies to every structure definition placed after where it is inserted (or until another #pragma pack overrides it), while GCC __attribute__s are defined locally to a type;
  • #pragma pack is less fine-grained than attributes: it cannot be applied to only a few members of a struct. In practice, however, this is rarely an issue, since you'll rarely need different alignment and packing settings for the members of a same struct.

In a very concise way, #pragma pack(n) is roughly equivalent to __attribute__((packed,aligned(n))): it defines both packing (compacting structures for memory-saving purposes) and minimal alignment. Hence the n (minimal alignment) on the pragma.

In principle, #pragma pack can be emulated using GCC attributes, but not the other way around, because of the finer control given by attributes.

Here's an example you can test on GCC: the first definition uses #pragma pack and the second one uses attributes. The layout is the same in both cases.

#include <stdio.h>
#include <stddef.h> // for offsetof()

#pragma pack(push, 4)
struct st {
  char c;
  double d;
  short e;
};
#pragma pack(pop) // disables the effect of #pragma pack from now on

struct st2 {
  char c __attribute__((packed,aligned(4)));
  double d __attribute__((packed,aligned(4)));
  short e __attribute__((packed,aligned(4)));
};

void main() {
  printf("offsetof(struct st, d) = %zu\n", offsetof(struct st, d));
  printf("offsetof(struct st2, d) = %zu\n", offsetof(struct st2, d));
  printf("offsetof(struct st, e) = %zu\n", offsetof(struct st, e));
  printf("offsetof(struct st2, e) = %zu\n", offsetof(struct st2, e));
}

GCC emits a warning on this example: ‘packed’ attribute ignored for field of type ‘char’. Indeed, a more concise and proper solution is to apply packed to the entire struct (as @Hagai did), which is equivalent1. Note, however, that you cannot simply apply aligned to the whole structure: the behavior is not equivalent to applying aligned to each field separately.

Note that, if you combine both (pragma + attributes) in the same structure definition, the algorithm is more complex, because it has to respect several constraints, which result in some min/max computations between (1) the alignment given by #pragma pack, (2) the member type's minimal alignment, and (3) aligned attributes declared in the field (if any).

1 From the GCC documentation:

Specifying the packed attribute for struct and union types is equivalent to specifying the packed attribute on each of the structure or union members.

Ferraro answered 14/10, 2016 at 12:26 Comment(5)
This is not equivalent, at least for aligned(2). See #43135890Hesitate
Could you please be more precise? I just tested on gcc 5.4.1 and 4.8.5, and replacing 4 with 2 in my example does give the same results for both structs. From your question, you seem to have applied the attribute to the struct itself, not to each field separately. In which case it does not work, hence why I put the attributes in each field separately.Ferraro
I added a clarification about the fact that aligned cannot be factored, even if packed can.Ferraro
You are right. I've simply misread your answer. Thanks for the clarification.Hesitate
__attribute__(aligned(n)) sets the minimum alignment, wheras pragma pack sets the maximum alignment, which is quite different.Exorcism
L
17

The #pragma pack(byte-alignment) effect each member of the struct as specified by the byte-alignment input, or on their natural alignment boundary, whichever is less.

The __attribute__((aligned(byte-alignment))) affect the minimum alignment of the variable (or struct field if specified within the struct)

I believe the following are equivalent

#define L1_CACHE_LINE 2

struct A
{
    u_int32_t   a   __attribute__ ( (aligned(L1_CACHE_LINE)) );
    u_int32_t   b   __attribute__ ( (aligned(L1_CACHE_LINE)) );
    u_int16_t   c   __attribute__ ( (aligned(L1_CACHE_LINE)) );       
    u_int16_t   d   __attribute__ ( (aligned(L1_CACHE_LINE)) );      
    u_int32_t   e   __attribute__ ( (aligned(L1_CACHE_LINE)) );     
};


#pragma pack(L1_CACHE_LINE)
struct A
{
    u_int32_t   a;  
    u_int32_t   b;  
    u_int16_t   c;  
    u_int16_t   d;  
    u_int32_t   e;  
};
#pragma pack()

where is A a __attritube__((aligned(L1_CACHE_LINE))) will insure u_int32_t a inside struct A will aligned with 2 byte but will not align the other variable in the same manner.

Reference:

  1. http://publib.boulder.ibm.com/infocenter/macxhelp/v6v81/index.jsp?topic=%2Fcom.ibm.vacpp6m.doc%2Fcompiler%2Fref%2Frnpgpack.htm
  2. http://www.khronos.org/registry/cl/sdk/1.0/docs/man/xhtml/attributes-variables.html
Leitmotif answered 6/1, 2013 at 6:32 Comment(2)
In one of my tests (stackoverflow.com/questions/39407675), I actually needed to add __attribute__((packed)) to the whole struct A to ensure the layout in both versions was the same. Could you please confirm that, and update the code example? I hesitate to do so in case my test is incomplete and could lead to other counterexamples.Ferraro
Be careful with using precompiler macros like L1_CACHE_LINE within pragmas.Kelantan
K
1

I fail to get the same results as in the other answers. The answer is perhaps more subtle than currently stated.

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

// Unaligned
struct st0 {
    int16_t total;
    int8_t d, e;
};

// Use MS pack
#pragma pack(push, 4)
struct st1 {
    int16_t total;
    int8_t d, e;
};
#pragma pack(pop)

// Align and pack individual attributes
struct st2 {
    int16_t total __attribute__((packed,aligned(4)));
    int8_t d __attribute__((packed,aligned(4)));
    int8_t e __attribute__((packed,aligned(4)));
};

// Align and pack individual attributes and entire struct
struct st3 {
    int16_t total __attribute__((packed,aligned(4)));
    int8_t d __attribute__((packed,aligned(4)));
    int8_t e __attribute__((packed,aligned(4)));
} __attribute__((packed,aligned(4)));

// Align and pack only the struct
struct st4 {
    int16_t total;
    int8_t d, e;
} __attribute__((packed,aligned(4)));

struct blah0 {
    int8_t a;
    struct st0 s;
};

struct blah1 {
    int8_t a;
    struct st1 s;
};

struct blah2 {
    int8_t a;
    struct st2 s;
};

struct blah3 {
    int8_t a;
    struct st3 s;
};

struct blah4 {
    int8_t a;
    struct st4 s;
};

void main() {
    printf("offsetof(struct st0, d) = %zu\n", offsetof(struct st0, d));
    printf("offsetof(struct st1, d) = %zu\n", offsetof(struct st1, d));
    printf("offsetof(struct st2, d) = %zu\n", offsetof(struct st2, d));
    printf("offsetof(struct st3, d) = %zu\n", offsetof(struct st3, d));
    printf("offsetof(struct st4, d) = %zu\n", offsetof(struct st4, d));
    printf("\n");
    printf("offsetof(struct st0, e) = %zu\n", offsetof(struct st0, e));
    printf("offsetof(struct st1, e) = %zu\n", offsetof(struct st1, e));
    printf("offsetof(struct st2, e) = %zu\n", offsetof(struct st2, e));
    printf("offsetof(struct st3, e) = %zu\n", offsetof(struct st3, e));
    printf("offsetof(struct st4, e) = %zu\n", offsetof(struct st4, e));
    printf("\n");
    printf("offsetof(struct blah0, s) = %zu\n", offsetof(struct blah0, s));
    printf("offsetof(struct blah1, s) = %zu\n", offsetof(struct blah1, s));
    printf("offsetof(struct blah2, s) = %zu\n", offsetof(struct blah2, s));
    printf("offsetof(struct blah3, s) = %zu\n", offsetof(struct blah3, s));
    printf("offsetof(struct blah4, s) = %zu\n", offsetof(struct blah4, s));
}

// Results in (compare with st1 and blah1).
//
// offsetof(struct st0, d) = 2
// offsetof(struct st1, d) = 2
// offsetof(struct st2, d) = 4
// offsetof(struct st3, d) = 4
// offsetof(struct st4, d) = 2
//
// offsetof(struct st0, e) = 3
// offsetof(struct st1, e) = 3
// offsetof(struct st2, e) = 8
// offsetof(struct st3, e) = 8
// offsetof(struct st4, e) = 3
//
// offsetof(struct blah0, s) = 2
// offsetof(struct blah1, s) = 2
// offsetof(struct blah2, s) = 4
// offsetof(struct blah3, s) = 4
// offsetof(struct blah4, s) = 4
//

This is with gcc (Ubuntu 11.2.0-19ubuntu1) 11.2.0.

Kelantan answered 19/9, 2022 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.