【问题标题】:Order of bytes in struct结构中的字节顺序
【发布时间】:2016-02-01 23:32:07
【问题描述】:

我对@9​​87654321@ 中字节的排序方式有点困惑。

假设我有以下结构:

struct container {
    int myint;
    short myshort;
    long mylong;
};

现在,我想初始化一个struct container 类型的变量,如下所示,除了我使用数组来完成。

struct container container1 = {.myint = 0x12345678,
                               .myshort = 0xABCD,
                               .mylong = 0x12345678};

假设sizeofintlong4short2

假设没有填充。

那么struct10 bytes 的布局会怎样呢?

是否取决于字节序?

会是这样的:

0x12345678 ABCD 12345678

或喜欢:

0x78563412 CDAB 78563412

我想做的是:我有以下 char 数组:

char buffer[10] = {0};

我想用数据手动填充这个数组,然后将memcpy 填充到struct

我应该这样做[1]

buffer[0] = 0x12345678 & 0xFF;
buffer[1] = 0x12345678 >> 8 & 0xFF;
buffer[2] = 0x12345678 >> 16 & 0xFF;
buffer[3] = 0x12345678 >> 24 & 0xFF;
...
buffer[9] = 0x12345678 >> 24 & 0xFF;

或者应该是[2]:

buffer[0] = 0x12345678 >> 24 & 0xFF;
buffer[1] = 0x12345678 >> 16 & 0xFF;
buffer[2] = 0x12345678 >> 8 & 0xFF;
buffer[3] = 0x12345678 & 0xFF;
...
buffer[9] = 0x12345678 & 0xFF;

在我做我的memcpy 之前:

memcpy(&container1, buffer, sizeof(container1);

而且,如果我正在写入一个数组并复制到 struct,它是否可以跨系统移植,尤其是在字节序方面?

编辑[1] 是否在小端机器上工作,而 [2] 在大端机器上工作?

【问题讨论】:

  • 不,它不是便携式的。是的,这取决于字节序。并且关于填充和类型大小的假设也会导致可移植性问题。
  • 为了强调没有可移植的方式,这意味着在同一个操作系统上从编译器到编译器没有可移植的方式,更不用说从操作系统到操作系统的可移植性了。
  • 当然取决于字节序!在您的“没有填充”假设之后,问题不再与结构类型有关。它只是关于在内存中表示整数。
  • 是否可以保证myintmyshort 放在mylong 之前?由于对齐问题,似乎订购它们mylongmyintmyshort 可能更有效 - 如果不允许实现进行此优化,那将是很遗憾的。

标签: c arrays pointers memory struct


【解决方案1】:

是否取决于字节序?

是的,它确实取决于机器的字节序。因此,您的逻辑将根据机器的字节顺序而改变。

由于结构填充,没有可移植的方式*来做到这一点。尽管不同的编译器确实提供了禁用结构填充的自定义方法。检查Force C++ struct to not be byte aligned

  • 您可以添加static_assert(需要C11 支持)只是为了确保您的代码不会编译,除非您的结构被紧密打包。你不会有可移植的代码,但你仍然可以确定,如果你的代码编译,它会正常运行。

    static_assert(sizeof(container) == sizeof(int) + sizeof(short) + sizeof(long));
    

【讨论】:

  • 不同编译器提供的禁用结构填充的方式是不可移植的——每个编译器都有自己的,除非它为了兼容性而模拟另一个。您可能需要注意 static_assert 需要 C11 编译器(或符合 C99 或 C90 的编译器中的非标准扩展)。
  • @JonathanLeffler 我已经在我的回答以及我链接的问题的答案中提到了 OP 寻求的解决方案不能移植。编辑以进一步强调它。
  • 我对在纯 C 问题中链接到纯 C++ 问题的看法不一。这至少是一个警告标志。
  • @JonathanLeffler 有一种方法可以使用 C 中的宏来执行 static_assert。请参阅我答案的最后一部分。这是我多年来使用的样板。我见过使用 switch 以外的其他东西的其他变体。 IMO,又一个 C++ 呃,stuff 不需要是 intrinsic
  • @CraigEstey 使用 C11,您无需提供自己的版本。 en.cppreference.com/w/c/error/static_assert
【解决方案2】:

还有另一个问题,即结构中的元素对齐问题。

你的结构有 gap 用于对齐。真正的布局就像你做的那样:

struct container {
    int myint;
    short myshort;
    char __pad__[2];  // padding to align mylong to 4/8 byte boundary
    long mylong;
};

如何使用union

union {
    struct container vals;
    char buf[10];
};

但是,为什么你要这样做?对于我能想到的几乎任何场景,都可能有一种更简洁的方法来获得效果。

当您说 array 时,您的意思是您想要初始化一个结构数组吗?可以这样做:

struct container conts[3] = {
    { .myint = 1, .myshort = 2, .mylong = 3 },
    { .myint = 4, .myshort = 5, .mylong = 6 },
    { .myint = 7, .myshort = 8, .mylong = 9 }
};

顺便说一句,一种方法可以在 C 语言中执行 static_assert

// compile time assertion -- if the assertion fails, we will get two case 0:'s
// which will cause the compiler to flag this
#define static_assert(_assert) \
    do { \
        switch (0) { \
        case (_assert): \
            break;
        case 0: \
            break; \
        } \
    } while (0)

【讨论】:

  • 而不是char buf[10];,最好使用char buf[sizeof (struct container)];
猜你喜欢
  • 1970-01-01
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多