我知道C中的结构可能不会像代码中那样布局内存。例如:
struct a {
short x;
int y;
};
假设2字节短路和4字节整数,可能实际占用内存中的8个字节,因为编译器希望在4字节边界上对齐成员...因此x之间有2个字节的松弛和y。
这使得读取和写入结构在语言,编译器和硬件之间不可移植。读取和写入它们的唯一方法是成员。是的,Endianness也是一个问题,交换必须在成员级别进行,但我们假设这不是问题。
Fortran有一个'序列'派生类型(结构)的说明符,它告诉编译器在给定内存中将成员布局。这允许便携式读取和写入派生类型。
我的问题是:有没有办法以便携式(可维护的)方式在C中做类似的事情?
答案 0 :(得分:5)
是的,Fortran 2003引入了bind(C)
说明符,它告诉编译器与伴随的C编译器完全相同
type, bind(C) :: a
components
end type
此外,iso_c_binding
模块(整个Fortran 2003 C互操作性的子部分)定义了可帮助您连接C和Fortran内部类型的常量:
use intrinsic :: iso_c_binding, only: c_short, c_int
type, bind(C) :: a
integer(c_short) :: x
integer(c_int) :: y
end type
据说这种类型与您的C结构可互操作。
C互操作性在编译器中得到广泛支持。很难找到不支持此功能但仍受其供应商支持的编译器。
混合C和Fortran时远离sequence
。根据标准,序列类型不能互操作。
答案 1 :(得分:2)
我所知道的没有100%清洁解决方案。我通常做的是创建两个名为pack_on.h
和pack_off.h
的头文件。要定义没有填充的结构:
#include <pack_on.h>
struct a {
short x;
int y;
} PACKED_STRUCTURE;
#include <pack_off.h>
头文件包含编译指示或满足编译器所需的任何内容,例如:
#ifdef _MSC_VER
#pragma pack(1)
#define PACKED_STRUCTURE /* nothing */
#endif
#ifdef __GNUC__
#define PACKED_STRUCTURE __attribute__(packed)
#endif
正如您所说,当二进制布局成为问题时,字节顺序通常也很重要。在上述解决方案中没有解决这个问题。
请注意,在整个代码中使用压缩结构也是一个坏主意。编译器在不强制执行这些打包结构时添加的填充对快速内存访问很有用。
在许多应用程序中,最好只在应用程序的一个层中使用这些打包结构,这些结构处理读取和写入这些打包结构,并将它们复制到正确对齐的结构中,然后再将它们传递到应用程序的核心中以进一步处理