结构的第16个字节上的16位int跳过一个字节

时间:2017-05-22 12:33:34

标签: c struct skip

在长度超过16字节的c结构中,如果它是2字节元素,则第16字节会出现问题。让我用代码解释一下:

struct test{
    unsigned int a : 16;
    unsigned int b : 16;
    unsigned int c : 16;
    unsigned int d : 16;
    unsigned int e : 16;
    unsigned int f : 16;
    unsigned int g : 16;
    unsigned int h : 8;
    unsigned int i : 16;
};

int main(int argc, char *argv[]){
    //bytes 0, 1, 16, 17 are all equal to 0x31 
    unsigned char *data = "1134567890123451189";
    struct test *testStructure = (struct test*) data;

    printf("%X %X\n", testStructure->a, testStructure->i);
    return 0;
}

输出:

3131 3831

为什么'我'不等于' a'? '我'跳过字节16并改为使用字节17和18。这是怎么回事?

1 个答案:

答案 0 :(得分:3)

我不会这样做因为:

  1. 位字段打包是实现定义的。参见C99§6.7.2.1p10:“单元内位域分配的顺序(从高阶到低阶或从低阶到高阶)是实现定义的”< / LI>
  2. 这违反了严格的别名规则。
  3. 在这种情况下,实际发生的事情i最有可能在4个字节(int的大小)上对齐。

    你可以禁用对齐,它应该会给你一个更好的结果:

    #pragma pack(1)
    struct test{
        unsigned int a : 16;
        unsigned int b : 16;
        unsigned int c : 16;
        unsigned int d : 16;
        unsigned int e : 16;
        unsigned int f : 16;
        unsigned int g : 16;
        unsigned int h : 8;
        unsigned int i : 16;
    };
    #pragma pack()
    
    int main(int argc, char *argv[]){
        //bytes 0, 1, 15, 16 are all equal to 0x31 
        unsigned char *data = "1134567890123451189";
        struct test *testStructure = (struct test*) data;
    
        printf("%X %X\n", testStructure->a, testStructure->i);
        return 0;
    }
    

    在clang上,它打印x86_64:

    3131 3131
    

    但是,此代码仍然非法,并不能保证在任何地方以这种方式工作。

    要解决位域问题,请尽量不使用位域(幸运的是,在您的特定情况下,这是可能的)。但遗憾的是,aliasing问题没有简单的解决办法;大多数依赖类型惩罚的人只需使用-fno-strict-aliasing编译(包括Linux内核人员)。其他人使用union跳过篮球,严格来说这仍然是非法的但是常见的习惯用语并得到了大多数编译器的良好支持:

    #include <stdio.h>
    #include <stdint.h>
    
    #pragma pack(1)
    struct test{
        uint16_t a;
        uint16_t b;
        uint16_t c;
        uint16_t d;
        uint16_t e;
        uint16_t f;
        uint16_t g;
        uint8_t  h;
        uint16_t i;
    };
    union u{
        struct test t;
        char str[17];
    };
    #pragma pack()
    
    int main(int argc, char *argv[]){
        //bytes 0, 1, 15, 16 are all equal to 0x31 
        char *data = "1134567890123451189";
        union u *testStructure = (union u*) data;
    
        printf("%X %X\n", testStructure->t.a, testStructure->t.i);
        return 0;
    }