C++结构对齐问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1455458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:02:46  来源:igfitidea点击:

C++ struct alignment question

c++memorygccvisual-c++

提问by NJChim

I have a predefined struct (actually several) where variables span across 32-bit word boundary. In Linux (and Windows using GCC) I am able to get my structs to pack to the correct size using 'attribute((packed))'. However I cannot get it to work the same way using VC++ and #pragma pack.

我有一个预定义的结构(实际上是几个),其中变量跨越 32 位字边界。在 Linux(和使用 GCC 的 Windows)中,我能够使用“attribute((packed))”将我的结构打包到正确的大小。但是,我无法使用 VC++ 和 #pragma pack 使其以相同的方式工作。

Using GCC this returns a correct size of 6 bytes:

使用 GCC 这将返回 6 个字节的正确大小:

struct
{
    unsigned int   a                : 3;
    unsigned int   b                : 1;
    unsigned int   c                : 15;
    unsigned int   troubleMaker     : 16;
    unsigned short padding          : 13;
} __attribute__((packed)) s;

Using VC++ this returns an incorrect size of 8 bytes

使用 VC++ 这会返回错误的 8 字节大小

#pragma pack(push)
#pragma pack(1)

struct
{
    unsigned int   a                : 3;
    unsigned int   b                : 1;
    unsigned int   c                : 15;
    unsigned int   troubleMaker     : 16;
    unsigned short padding          : 13;
} s;

#pragma pack(pop)

I can get things to work by splitting 'troubleMaker' across the boundary manually but I'd prefer not to. Any ideas?

我可以通过手动跨边界拆分“troubleMaker”来使事情发挥作用,但我不想这样做。有任何想法吗?

回答by DigitalRoss

Crazy idea: just write a C99 or C++03 -conforming program in the first place

疯狂的想法:首先只写一个符合 C99 或 C++03 的程序



I would suggest not using vendor-specific C language extensions to match device or network bit formats. Even if you get the fields to line up using a series of one-per-vendor language extensions, you still have byte order to worry about, and you still have a struct layout that requires extra instructions to access.

我建议不要使用特定于供应商的 C 语言扩展来匹配设备或网络位格式。即使您使用一系列每个供应商的语言扩展来排列字段,您仍然需要担心字节顺序,并且您仍然有一个需要额外指令才能访问的结构布局。

You can write a C99 conforming program that will work on any architecture or host and at maximum speed and cache efficiency by using the standardized C API string and memory copy functions and the Posix hton and ntoh functions.

通过使用标准化的 C API 字符串和内存复制函数以及 Posix hton 和 ntoh 函数,您可以编写符合 C99 的程序,该程序可以在任何架构或主机上运行,​​并以最大速度和缓存效率运行。

A good practice is to use the following functions for which there exist published standards:

一个好的做法是使用以下已发布标准的函数:

C99: memcpy(), Posix: htonl(), htons(), ntohl(), ntohs()

Update:here is some code that should work the same everywhere. You may need to get <stdint.h>from this projectif Microsoft stillhasn't implemented it for C99, or just make the usual assumptions about int sizes.

更新:这里有一些代码应该在任何地方都一样。如果微软没有为 C99 实现它,你可能需要<stdint.h>从这个项目中得到它,或者只是对 int 大小做出通常的假设。

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <arpa/inet.h>

struct packed_with_bit_fields {  // ONLY FOR COMPARISON
    unsigned int   a        : 3;
    unsigned int   b        : 1;
    unsigned int   c        : 15;
    unsigned int   troubleMaker : 16;
    unsigned short padding  : 13;
} __attribute__((packed));       // USED ONLY TO COMPARE IMPLEMENTATIONS

struct unpacked { // THIS IS THE EXAMPLE STRUCT
    uint32_t a;
    uint32_t b;
    uint32_t c;
    uint32_t troubleMaker;
}; // NOTE NOT PACKED

struct unpacked su;
struct packed_with_bit_fields sp;
char *bits = "Lorem ipsum dolor";

int main(int ac, char **av) {
  uint32_t x;   // byte order issues ignored in both cases

  // This should work with any environment and compiler
  memcpy(&x, bits, 4);
  su.a = x & 7;
  su.b = x >> 3 & 1;
  su.c = x >> 4 & 0x7fff;
  memcpy(&x, bits + 2, 4);
  su.troubleMaker = x >> 3 & 0xffff;

  // This section works only with gcc
  memcpy(&sp, bits, 6);
  printf( sp.a == su.a
      &&  sp.b == su.b
      &&  sp.c == su.c
      &&  sp.troubleMaker == su.troubleMaker
      ? "conforming and gcc implementations match\n" : "huh?\n");
  return 0;
}

回答by Paul Lalonde

Alignment and ordering of bitfields are notoriously implementation-specific. It is muchsafer to declare a normal integer field and manipulate the "bitfields" within using masks and bitwise (| & ^) operators .

众所周知,位域的对齐和排序是特定于实现的。这是很多安全声明一个正常的整型字段和操作使用内口罩和按位“位域”(|&^)运营商。

回答by JaredPar

I don't believe this behavior is supported in Visual Studio. In addiction to the pack macro I tried using __declspec(align(1))and got the same behavior. I think you are stuck with 12 bytes or re-ordering your structure a bit.

我认为 Visual Studio 不支持这种行为。沉迷于包宏,我尝试使用__declspec(align(1))并得到了相同的行为。我认为你被 12 个字节困住了,或者对你的结构重新排序了一下。

回答by DigitalRoss

Shorter example with only conforming code

只有符合代码的较短示例



struct unpacked {  // apparently my other example was too long and confusing
    uint32_t a;    // ...here is a much shorter example with only the conforming
    uint32_t b;    // ...code. (The other program had the gcc-specific declaration,
    uint32_t c;    // but only for test code. Still, it was a bit long.)
    uint32_t troubleMaker;
};

struct unpacked su;
char *bits = "Lorem ipsum dolor";

void f(void) {
  uint32_t x;

  memcpy(&x, bits, 4);
  su.a = x & 7;
  su.b = x >> 3 & 1;
  su.c = x >> 4 & 0x7fff;
  memcpy(&x, bits + 2, 4);
  su.troubleMaker = x >> 3 & 0xffff;
  return 0;
}

回答by Anton Tykhyy

I believe VC++ doesn't support this, and I have grave doubts whether GCC's behaviour in this respect is actually standard.

我相信 VC++ 不支持这一点,我严重怀疑 GCC 在这方面的行为是否真的是标准的。

回答by Goz

If it absoloutely defnitely needs to be 6 bytes then define it as 3 shorts and get the data out yourself ... it won't slow things down ... the compiler is just doing this anyway ...

如果它绝对需要是 6 个字节,那么将它定义为 3 个 shorts 并自己获取数据......它不会减慢速度......无论如何编译器只是这样做......