结构的第16个字节上的16bit int跳过一个字节[重复]

Posted

技术标签:

【中文标题】结构的第16个字节上的16bit int跳过一个字节[重复]【英文标题】:16bit int on 16th byte of structure skips a byte [duplicate] 【发布时间】:2017-10-22 02:50:47 【问题描述】:

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

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

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

【问题讨论】:

演员阵容完全是狂野的,它调用了未定义的行为,因为它违反了严格的别名。 字节 0,1,15 和 16 是 '1' (0x31),所以不是字节 17,它是 '8'。此外,这不仅仅是一个结构,而是一个位域。 见这里***.com/questions/1490092/…,你想要做的是(据我所知)真的不便携。您必须查看编译器的文档才能弄清楚到底发生了什么。 您看到“跳过”字节的原因是因为每个字段都必须在正确的边界上“对齐”。因此,在 8 位字段之后是一个“虚拟”对齐字节,因此最后的 16 位字段正确对齐。此外,您的硬件体系结构的“字节序”是这样的,即每个 16 位字段具有在 char 数组中看到的顺序的字节,但 16 位字段首先读取最右边的字节。如果您以“12”开始 char 数组 data,则“字节序”会更加明显(或者如果您不熟悉“字节序”,则会更加混乱 【参考方案1】:

我不会这样做,因为:

    位域打包是实现定义的。请参阅 C99 §6.7.2.1p10:“单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的” /li> 这违反了严格的别名规则。

实际发生的情况很可能在这种情况下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 内核人员)进行编译。其他人则使用unions 跳过箍,严格来说仍然是非法的,但它是常见的习惯用法,并且得到大多数编译器的良好支持:

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

【讨论】:

很好地解释了为什么 OP 方法正确,您将如何解释 正确位字段初始化? @ryyker 你会如何解释正确的位域初始化?这样的事情是否存在?位字段是如此特定于实现且不可移植,我认为没有任何真正的“正确”方法可以以任何可移植方式初始化它们。如果它不便携,它是否合适也没关系。 谢谢,这是一个很好的解释。非常感谢。

以上是关于结构的第16个字节上的16bit int跳过一个字节[重复]的主要内容,如果未能解决你的问题,请参考以下文章

c语言,为啥int类型在16位系统中占2个字节,在32位系统中占4个字节?

位字节字双字

进制,字节,字长,位数

存储单位

关于内存单元字节字符等

bit byte 字 字节 关系 简列