Visual C++ 相当于 GCC 的 __attribute__ ((__packed__))

问题描述 投票:0回答:7

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

RealView ARM 编译器有“__packed”
Gnu C 编译器有“__attribute__ ((__packed__))”
Visual C++ 没有等效项,它只有“#pragma pack(1)”

我需要一些可以放入 struct 定义中的东西。

有任何信息/黑客/建议吗?蒂亚...

c++ c visual-c++ gcc data-structures
7个回答
115
投票

您可以为

GNU GCC
MSVC
定义 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

并像这样使用它:

PACK(struct myStruct
{
    int a;
    int b;
});

38
投票

我不知道一个巧妙的方法,但你可能会做这样可怕的事情:

#include "packed.h"
struct Foo { /* members go here */ } PACKED;
#include "endpacked.h"

然后对于MSVC,packed.h:

#define PACKED
#pragma pack(push,1)

endpacked.h

#pragma pack(pop)
#undef PACKED

对于 gcc,packed.h:

#define PACKED __attribute__ ((__packed__))

endpacked.h:

#undef PACKED

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

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


22
投票

我知道这个问题现在已经很老了,但我相信有比之前发布的更好的解决方案。毕竟,可以将 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

然后可以像这样使用:

typedef PACKED_STRUCT() { short a; int b } my_struct_t;
PACKED_STRUCT(my_other_struct) { short a; int b };

等等

这里的关键是__pragma的使用只需要在结构体的声明行周围。如果给定了结构名称,则需要包含结构名称,因此该名称是宏的参数。当然,这很容易扩展到枚举/类,我将把它作为练习留给读者!

pack 文档 MSDN 页面上的测试程序对于验证这一点很有用。

编辑

事实证明,在我的测试中,我在 Windows 上使用了英特尔编译器。使用 icl.exe 这种方法可以正常工作,但使用 Microsoft 编译器 (cl.exe) 则不行(在 2010 年和 2013 年进行了测试)。


16
投票

你可以反过来做,因为 GCC 支持 VC++ 包相关的编译指示。请查看此处了解更多信息。

提取...

为了与 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
返回默认布局。


6
投票

另一个解决方案,取决于您需要支持的编译器,请注意 GCC 至少从版本 4.0.4 开始就支持 Microsoft 风格的打包编译指示(可以在 gnu.org 上找到版本 3.4.6 和 4.0 的在线文档。 4 - 前者未描述编译指示,后者则在后者中描述)。 这让您只需在结构定义之前使用

#pragma pack(push,1)
并在定义之后使用
#pragma pack(pop)
即可在其中任一版本中进行编译。


4
投票

为什么你需要在结构中添加一些东西?

我认为

#pragma pack(1)
是一样的,还是我错过了什么?

你可以这样做:

struct Foo
{
#pragma pack(push, 1)
int Bar;
#pragma pack(pop)
};

但是看起来很丑。


0
投票

限制:使用下面的 MSC 定义,结构体中间不能有“#if”,编译器会感到困惑...

#define PACKED_STRUCT( 声明 ) __pragma( pack(push, 1) ) struct 声明 __pragma( pack(pop) )

PACK(typedef struct
    {
    int i;
 #if THIS_CONFUSES_THE_COMPILER
    float f;
#endif
    }) s_type;
© www.soinside.com 2019 - 2024. All rights reserved.