繁体   English   中英

不同操作系统的不同结果

[英]Different result in different OS

[ 2016.03.17 更新] 抱歉,为了简单起见,我跳过了错误检查。 我检查了错误,这是完整的代码。

#define MAX_DATA 512
#define MAX_ROWS 100
typedef struct Database {
    Address rows[MAX_ROWS];
    int num; // Number of record in the DB.
} Database;

typedef struct Address {
    int id;
    int set;
    char name[MAX_DATA];
    char email[MAX_DATA];
} Address;

Database *db_ptr;
FILE *file;
int return_value;
// Write to file
db_ptr = (Database*)malloc(sizeof(Database));
if(!db_ptr) {
    printf("Memory error!\n");
}
for(int i = 0; i < MAX_ROWS; i++) {
    db_ptr->rows[i].id = i;
    db_ptr->rows[i].set = 0;
}
char *filename = "test.db";
file = fopen(filename, "w");
if(!file) {
    printf("Open(w) %s fail!\n", filename);
}
return_value = fwrite(db_ptr, sizeof(Database), 1, file);
printf("The return value from fwrite = %d\n", return_value);
free(db_ptr);
fclose(file);
// Read from file
db_ptr = (Database*)malloc(sizeof(Database));
if(!db_ptr) {
    printf("Memory error!\n");
}
file = fopen(filename, "r+");
if(!file) {
    printf("Open(r+) %s fail!\n", filename);
}
return_value = fread(db_ptr, sizeof(Database), 1, file);
printf("(1)The return value from fread = %d\n", return_value);
rewind(file);
return_value = fread(db_ptr, 1, sizeof(Database), file);
printf("(2)The return value from fread = %d\n", return_value);
printf("Sizeof(Database) = %lu\n", sizeof(Database));
free(db_ptr);
fclose(file);

结果是

"The return value from fwrite = 1"
"(1)The return value from fread = 1"
"(2)The return value from fread = 103204"
"Sizeof(Database) = 103204"

在 Ubuntu15.04(64 位)中使用带有 -std=c99 的 gcc,以及

"The return value from fwrite = 1"
"(1)The return value from fread = 0"
"(2)The return value from fread = 26832"
"Sizeof(Database) = 103204"

在 Windows7(64 位)中使用带有 -std=c99 的 MinGW。

test.db 的大小在 Ubuntu 中为 103204 字节,在 Windows 中为 103205 字节。 似乎无法读取数据库的整个结构。

我的问题是这个程序如何在不同的环境中有不同的行为?

C struct是旨在在内存中使用的数据的表示形式。 一旦您的数据以任何方式“离开”内存,您就需要使用编组(或通常也称为序列化,这里不值得讨论这些差异)。

为此,您需要将数据转换为适合您将数据传输到的介质的格式,在本例中为磁盘。

struct不适合主要是因为以下问题:

  • 填充 填充结构以将成员与可以更快访问它们的地址对齐,或者甚至使访问成为可能(在某些体系结构上)。
  • 字节序 存储长度超过单个字节的数据的方式。 这因架构而异。

我用 C 代码示例编写了一个答案,清楚地解决了这些问题,请参见此处

出于这个原因,在这种情况下使用freadfwrite的整个方法是有缺陷的 当您的程序仍在运行时,您可以暂时使用这种类型的数据存储到磁盘。 一旦数据可以与不同系统(同一系统的版本、不同机器、操作系统、库等)共享/交换或存储在程序的连续运行之间,您就需要采用正确的编组。

关于您的具体情况:对fread的调用可能会返回 0,因为它无法读取完整的项目。 sizeof(Database)在这些环境中可能不同。

尽管这只是猜测,因为您似乎没有进行错误检查,尤其是在打开文件方面。 你也可以看看errno (考虑使用strerror )提供了什么。

user3121023和 Peter 提出的一个很好的观点是打开模式:在 Windows 上,如果文件不是以所谓的二进制模式打开,则会对您读取或写入文件的数据进行一些转换。 例如,这意味着如果您的数据包含一个等于'\\n'的字节,那么 Windows将添加一个额外的'\\r' (回车)。

首先,您需要打开用于二进制 I/O 的文件(在提供给fopen()的模式字符串中包含一个'b'字符)。 没有它,文件将以文本模式打开,除其他外,它会在系统之间以不同的方式转换换行符等字符。

其次,更重要的是,您需要使用sizeof()这一事实暗示了这一点。 sizeof产生的结果是实现定义的,对于根据定义大小为1char类型的任何内容。 sizeof int是实现定义的。 sizeof还为struct类型产生不同的值,因为不同类型的成员之间可能存在填充,以满足所有struct成员的对齐要求。 在 C 标准中,这些东西是实现定义的结果是它们在实现之间(即在编译器和主机系统之间)有所不同。

这解释了为什么您在系统之间获得不同的大小 - 在不同的系统上,每个操作正在写入或读取不同数量的数据。

为了处理这些事情,你要么需要使用技术来确保所有的东西都是相同的大小(这通常是可能的,但很困难,因为某些类型的某些属性(如intstruct padding)可以通过编译选项进行更改在某些编译器上)或进行一些编组(将内存中的struct类型转换为某种一致的二进制格式以进行输出)和解组(相反的过程)。 输入过程需要与输出过程相反(从文件中提取数据作为字符数组,并解释数据以重建您的struct )。

即使两个系统具有相同的大小(对于一切),也存在基本类型(如int )在内存中如何表示的问题,例如字节序。 这也需要用输入和输出的方法来处理。

一种更简单的方法是使用文本模式文件和格式化的 I/O。 这具有文件(主要)可传输的优点,但也意味着文件更大。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM