编译器:GNU GCC
应用程序类型:控制台应用程序
语言:C
平台:Win7和Linux Mint
我写了一个程序,我想在Win7和Linux下运行。程序将C结构写入文件,我希望能够在Win7下创建该文件并在Linux中读取它,反之亦然。
到目前为止,我已经了解到使用fwrite()编写完整的结构将几乎100%保证不会被其他平台正确读回。这是由于填充和其他原因造成的。
我自己定义了所有结构,他们(现在,在我在此论坛上的上一个问题之后)都有类型为int32_t,int64_t和char的成员。我正在考虑为每个结构编写一个WriteStructname()函数,它将把各个成员编写为int32_t,int64_t和char到outputfile。同样,一个ReadStructname()函数从文件中读取各个struct成员并再次将它们复制到一个空结构中。
这种方法有用吗?我更喜欢对源代码进行最大程度的控制,所以除非我真的需要,否则我不会寻找库或其他依赖项来实现这一点。
感谢您阅读
答案 0 :(得分:1)
以数据方式将数据写入文件是最佳方法,因为struct
将differ due to alignment and packing differences between compilers.
然而,即使使用您计划使用的方法,仍然存在潜在的缺陷,例如系统之间的endianness不同,或encoding schemes不同(即:two's complement versus one's complement encoding of signed numbers)。
如果你打算这样做,你应该考虑像JSON parser to encode and decode your data这样的东西,这样你就不会因为上面提到的问题而腐败它。
祝你好运!答案 1 :(得分:1)
如果您使用GCC或任何其他支持"打包"结构,只要你避免在结构中使用除[u]intX_t
类型之外的任何东西,并在类型大于8位的任何字段中执行字节顺序修复,你就是平台安全的:)
这是一个示例代码,您可以在平台之间获得可移植性,不要忘记手动编辑字节序UIP_BYTE_ORDER
。
#include <stdint.h>
#include <stdio.h>
/* These macro are set manually, you should use some automated detection methodology */
#define UIP_BIG_ENDIAN 1
#define UIP_LITTLE_ENDIAN 2
#define UIP_BYTE_ORDER UIP_LITTLE_ENDIAN
/* Borrowed from uIP */
#ifndef UIP_HTONS
# if UIP_BYTE_ORDER == UIP_BIG_ENDIAN
# define UIP_HTONS(n) (n)
# define UIP_HTONL(n) (n)
# define UIP_HTONLL(n) (n)
# else /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */
# define UIP_HTONS(n) (uint16_t)((((uint16_t) (n)) << 8) | (((uint16_t) (n)) >> 8))
# define UIP_HTONL(n) (((uint32_t)UIP_HTONS(n) << 16) | UIP_HTONS((uint32_t)(n) >> 16))
# define UIP_HTONLL(n) (((uint64_t)UIP_HTONL(n) << 32) | UIP_HTONL((uint64_t)(n) >> 32))
# endif /* UIP_BYTE_ORDER == UIP_BIG_ENDIAN */
#else
#error "UIP_HTONS already defined!"
#endif /* UIP_HTONS */
struct __attribute__((__packed__)) s_test
{
uint32_t a;
uint8_t b;
uint64_t c;
uint16_t d;
int8_t string[13];
};
struct s_test my_data =
{
.a = 0xABCDEF09,
.b = 0xFF,
.c = 0xDEADBEEFDEADBEEF,
.d = 0x9876,
.string = "bla bla bla"
};
void save()
{
FILE * f;
f = fopen("test.bin", "w+");
/* Fix endianness */
my_data.a = UIP_HTONL(my_data.a);
my_data.c = UIP_HTONLL(my_data.c);
my_data.d = UIP_HTONS(my_data.d);
fwrite(&my_data, sizeof(my_data), 1, f);
fclose(f);
}
void read()
{
FILE * f;
f = fopen("test.bin", "r");
fread(&my_data, sizeof(my_data), 1, f);
fclose(f);
/* Fix endianness */
my_data.a = UIP_HTONL(my_data.a);
my_data.c = UIP_HTONLL(my_data.c);
my_data.d = UIP_HTONS(my_data.d);
}
int main(int argc, char ** argv)
{
save();
return 0;
}
这是保存的文件转储:
fanl@fanl-ultrabook:~/workspace-tmp/test3$ hexdump -v -C test.bin
00000000 ab cd ef 09 ff de ad be ef de ad be ef 98 76 62 |..............vb|
00000010 6c 61 20 62 6c 61 20 62 6c 61 00 00 |la bla bla..|
0000001c
答案 2 :(得分:0)
这是一个很好的方法。如果所有字段都是特定大小的整数类型,例如int32_t
,int64_t
或char,并且您在数组中读取/写入适当数量的字段,那么您应该没问题。
你需要注意的一件事是字节序。任何整数类型都应以已知的字节顺序写入,并以相应的字节顺序读回相关系统。最简单的方法是使用16位整数的ntohs
和htons
函数以及32位整数的ntohl
和htonl
函数。对于64位整数,没有相应的标准函数,但这不应该难以编写。
以下是如何为64位编写这些函数的示例:
uint64_t htonll(uint64_t val)
{
uint8_t v[8];
uint64_t *result = (uint64_t *)v;
int i;
for (i=0; i<8; i++) {
v[i] = (uint8_t)(val >> ((7-i) * 8));
}
return *result;
}
uint64_t ntohll(uint64_t val)
{
uint8_t *v = (uint8_t *)&val;
uint64_t result = 0;
int i;
for (i=0; i<8; i++) {
result |= (uint64_t)v[i] << ((7-i) * 8);
}
return result;
}