Visual C++相当于GCC的__attribute __((__ package___))

Mal*_*glu 46 c c++ gcc visual-c++ data-structures

对于某些编译器,有一个结构的打包说明符,例如::

RealView ARM compiler has "__packed"
Gnu C Compiler has "__attribute__ ((__packed__))"
Visual C++ has no equivalent, it only has the "#pragma pack(1)"

我需要一些我可以放入结构定义的东西.

任何信息/黑客/建议?TIA ...

Ste*_*eph 75

您可以为GNU gcc定义这样的PACK

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif
Run Code Online (Sandbox Code Playgroud)

和Visual C++一样:

PACK(struct myStruct
{
    int a;
    int b;
});
Run Code Online (Sandbox Code Playgroud)

并像这样使用它:

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif
Run Code Online (Sandbox Code Playgroud)

  • @EricAngell如果你编写`PACK(typedef struct {int x;})MyStruct;`,上面的函数适用于typedef.您也可以使用`#define PACK(...)__ VA_ARGS__ __attribute __((__ packed __))`等来处理声明体中的逗号. (5认同)
  • 我喜欢这个解决方案,然而,我花了很长时间才弄清楚MSVC++处理位域的方式与GCC略有不同.我使用各种大小的"int"位域(1,7,8,16)并发现MSVC++仍然希望将所有内容打包成4字节边界.通过转换为char,1,7和8,以及16的缩写,然后得到了我期待的包装. (2认同)

Ste*_*sop 28

我不知道这样做的光滑方式,但你可能会做一些可怕的事情:

#include "packed.h"
struct Foo { /* members go here */ } PACKED;
#include "endpacked.h"
Run Code Online (Sandbox Code Playgroud)

那么对于MSVC,packed.h:

#define PACKED
#pragma pack(push,1)
Run Code Online (Sandbox Code Playgroud)

endpacked.h

#pragma pack(pop)
#undef PACKED
Run Code Online (Sandbox Code Playgroud)

对于gcc,packed.h:

#define PACKED __attribute__ ((__packed__))
Run Code Online (Sandbox Code Playgroud)

endpacked.h:

#undef PACKED
Run Code Online (Sandbox Code Playgroud)

从根本上说,包装过于依赖平台.假设您的压缩结构中包含8位字段,并考虑一些具有16位字节的系统.它只能通过打包来形成一个表示数据的结构 - 你必须知道在两个系统之间传输时如何将8位字节转换为16位字节.16位机器上的结构可能需要位域,在这种情况下,您必须知道实现如何实现它们.

因此,如果代码通常是可移植的,您可能只需要在头文件的特定于平台的部分中定义所需的任何打包结构.或者更确切地说,构造您的代码,以便未来的端口可以做到这一点,如果必须的话.

  • 我想我会采取"开始/结束必须匹配","必须做了偶数次",谢谢;-).正如我在答案中所说,原则上您需要一个特定于平台的结构定义.但实际上,这涵盖了提问者目前关注的三个编译器,它允许更多的通用性,而不仅仅是大量的复制/粘贴,并且与一堆`#if _WIN32 #elif __GNUC__`等相比,它并不是非常糟糕.围绕每个结构定义.`#include`是C中用于抽象或重用包含预处理程序指令的代码片段的正确/唯一方式. (8认同)
  • 从技术上讲,你可以在一个标题中完成所有这些:`#ifndef PACKED` /`#define PACKED` /`#else/*!PACKED*/`/`#undef PACKED` /`#endif/*PACKED*/`然后包括两次相同的标题,一次启用,一次禁用.这种做法的有用性/清洁/健全是值得商榷的,但你所建议的做法也是如此. (5认同)

Sam*_*sen 15

我知道这个问题现在已经过时了,但我相信有一个比之前发布的更好的解决方案.毕竟,可以将pragma放在结构声明行中的MSVC案例中.考虑以下:

#ifdef _MSC_VER
#  define PACKED_STRUCT(name) \
    __pragma(pack(push, 1)) struct name __pragma(pack(pop))
#elif defined(__GNUC__)
#  define PACKED_STRUCT(name) struct __attribute__((packed)) name
#endif
Run Code Online (Sandbox Code Playgroud)

然后就可以这样使用:

typedef PACKED_STRUCT() { short a; int b } my_struct_t;
PACKED_STRUCT(my_other_struct) { short a; int b };
Run Code Online (Sandbox Code Playgroud)

等等

这里的关键是__pragma的使用只需要围绕struct的声明行.如果给出一个结构名称,则需要包含结构名称,因此该名称是宏的参数.当然,这很容易扩展到enum/class,我将把它作为练习留给读者!

包文档MSDN页面上的测试程序可用于验证这一点.

编辑

在我的测试中,我在Windows上使用了英特尔编译器.使用icl.exe这种方法没有问题,但使用Microsoft编译器(cl.exe),它没有(使用2010和2013测试).


Aut*_*act 9

你可以反过来做,因为GCC支持VC++包相关的pragma.看这里了解更多信息.

提取...

为了与Microsoft Windows编译器兼容,GCC支持一组#pragma指令,这些指令可以更改随后定义的结构(除了零宽度位域除外),联合和类的成员的最大对齐.下面的n值始终需要是2的小幂,并以字节为单位指定新的对齐方式.

#pragma pack(n) 只需设置新的对齐方式.

#pragma pack()将对齐设置为编译开始时生效的对齐(另请参阅命令行选项, -fpack-struct[=<n>]请参阅代码选项选项).

#pragma pack(push[,n]) 将当前对齐设置推送到内部堆栈,然后可选地设置新对齐.

#pragma pack(pop) 将对齐设置恢复为保存在内部堆栈顶部的设置(并删除该堆栈条目).

注意,#pragma pack([n])这不会影响这个内部堆栈; 因此可以#pragma pack(push)跟随多个 #pragma pack(n)实例并由单个最终确定#pragma pack(pop).

一些目标,例如i386和powerpc,支持将ms_struct #pragma 结构列为记录的结构__attribute__((ms_struct)).

#pragma ms_struct on 打开声明的结构的布局.

#pragma ms_struct off 关闭声明的结构的布局.

#pragma ms_struct reset 回到默认布局.


Tom*_*Tom 6

根据您需要支持的编译器,另一个解决方案是注意到GCC至少从4.0.4版本开始支持Microsoft样式的打包pragma(在线文档可从gnu.org获得,版本为3.4.6和4.0.4) - 前者没有描述pragma,后者也是如此.这允许您#pragma pack(push,1)在结构定义之前和定义#pragma pack(pop)之后使用,它将在任一个中编译.