cgccpragmapragma-pack

pragma pack(n) vs pragma pack(push,n)


What is the difference between

  1. pragma pack(n) and pragma pack(push,n)
  2. #pragma pack(pop) and #pragma pack()

Can someone explain through an example ? Which should be used when ?

I went through https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html#:~:text=%23pragma%20pack(push%5B%2C,and%20removes%20that%20stack%20entry) already but looking out for more explanation.


Solution

  • If you use push and pop, you can "stack" multiple packing specifications. Without, you can just return to the default:

    #include <stddef.h>
    #include <stdio.h>
    
    struct pushed_default {
        char c;
        long l;
    };
    #pragma pack(push, 2)
    struct pushed_2 {
        char c;
        long l;
    };
    #pragma pack(push, 1)
    struct pushed_1 {
        char c;
        long l;
    };
    #pragma pack(push, 4)
    struct pushed_4 {
        char c;
        long l;
    };
    #pragma pack(pop)
    struct popped_a {
        char c;
        long l;
    };
    #pragma pack(pop)
    struct popped_b {
        char c;
        long l;
    };
    #pragma pack(pop)
    struct popped_c {
        char c;
        long l;
    };
    
    #pragma pack(2)
    struct pack_2 {
        char c;
        long l;
    };
    #pragma pack(1)
    struct pack_1 {
        char c;
        long l;
    };
    #pragma pack(4)
    struct pack_4 {
        char c;
        long l;
    };
    #pragma pack()
    struct pack_a {
        char c;
        long l;
    };
    #pragma pack()
    struct pack_b {
        char c;
        long l;
    };
    #pragma pack()
    struct pack_c {
        char c;
        long l;
    };
    
    int main(void) {
        printf("%zu\n\n", offsetof(struct pushed_default, l));
    
        printf("%zu\n", offsetof(struct pushed_2, l));
        printf("%zu\n", offsetof(struct pushed_1, l));
        printf("%zu\n", offsetof(struct pushed_4, l));
        printf("%zu\n", offsetof(struct popped_a, l));
        printf("%zu\n", offsetof(struct popped_b, l));
        printf("%zu\n\n", offsetof(struct popped_c, l));
    
        printf("%zu\n", offsetof(struct pack_2, l));
        printf("%zu\n", offsetof(struct pack_1, l));
        printf("%zu\n", offsetof(struct pack_4, l));
        printf("%zu\n", offsetof(struct pack_a, l));
        printf("%zu\n", offsetof(struct pack_b, l));
        printf("%zu\n", offsetof(struct pack_c, l));
    }
    

    The result talks for itself:

    $ gcc -Wall -Wpedantic packed.c -O3 -s -o packed
    $ ./packed 
    8
    
    2
    1
    4
    1
    2
    8
    
    2
    1
    4
    8
    8
    8
    

    You can use push and pop all the time. Especially you can #include headers with these directives without fear of breaking other pack adjustments.

    This is the relevant part of the documentation:

    1. #pragma pack(push[,n]) pushes the current alignment setting on an internal stack and then optionally sets the new alignment.
    2. #pragma pack(pop) restores the alignment setting to the one saved at the top of the internal stack (and removes that stack entry). Note that #pragma pack([n]) does not influence this internal stack; thus it is possible to have #pragma pack(push) followed by multiple #pragma pack(n) instances and finalized by a single #pragma pack(pop).

    Unfortunately the maximum number of pushes is not documented. However, in practice you will not reach it.