简体   繁体   English

不同操作系统的不同结果

[英]Different result in different OS

[ Update 2016.03.17 ] Sorry for that I skipped the error checking for simplicity. [ 2016.03.17 更新] 抱歉,为了简单起见,我跳过了错误检查。 I had check the errors, here is the complete code.我检查了错误,这是完整的代码。

#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 results are结果是

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

in Ubuntu15.04(64-bits) using gcc with -std=c99, and在 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"

in Windows7(64-bits) using MinGW with -std=c99.在 Windows7(64 位)中使用带有 -std=c99 的 MinGW。

The size of the test.db are 103204 bytes in Ubuntu and 103205 bytes in Windows. test.db 的大小在 Ubuntu 中为 103204 字节,在 Windows 中为 103205 字节。 It seems fail to read the whole structure of Database.似乎无法读取数据库的整个结构。

My question is that how this program has different behavior in different environment?我的问题是这个程序如何在不同的环境中有不同的行为?

C struct s are representations of data that are intended to be used only in memory. C struct是旨在在内存中使用的数据的表示形式。 As soon as your data "leaves" memory in any way, you need to employ marshalling (or often also called serialization, the differences are not worth discussing here).一旦您的数据以任何方式“离开”内存,您就需要使用编组(或通常也称为序列化,这里不值得讨论这些差异)。

In order to do so, you need to convert your data into a format suitable for the medium you transfer it to, the disk in this case.为此,您需要将数据转换为适合您将数据传输到的介质的格式,在本例中为磁盘。

A struct is not suitable because of mostly these issues: struct不适合主要是因为以下问题:

  • Padding .填充 Structures are padded to align members to addresses where they can be accessed faster, or to make access even possible (on some architectures).填充结构以将成员与可以更快访问它们的地址对齐,或者甚至使访问成为可能(在某些体系结构上)。
  • Endianness .字节序 The way data that is longer than a single byte is store.存储长度超过单个字节的数据的方式。 This varies between architectures.这因架构而异。

I've written an answer with C code example that clearly addresses these issues, see here .我用 C 代码示例编写了一个答案,清楚地解决了这些问题,请参见此处

For this reasons, the whole approach using fread and fwrite in this situation is flawed .出于这个原因,在这种情况下使用freadfwrite的整个方法是有缺陷的 You can employ this style of data storage to disk to temporarily while your program is still running.当您的程序仍在运行时,您可以暂时使用这种类型的数据存储到磁盘。 As soon as the data could be shared / exchanged with different systems (versions of the same system, different machines, operating systems, libraries, ...) or is stored between consecutive runs of your program you need to employ correct marshalling.一旦数据可以与不同系统(同一系统的版本、不同机器、操作系统、库等)共享/交换或存储在程序的连续运行之间,您就需要采用正确的编组。

Concerning your specific case: The call to fread probably returns 0 because it is not able to read a complete item.关于您的具体情况:对fread的调用可能会返回 0,因为它无法读取完整的项目。 sizeof(Database) might be different between these environments. sizeof(Database)在这些环境中可能不同。

Though this is only guessing, because you seem to have no error checking, especially for opening the files, in place.尽管这只是猜测,因为您似乎没有进行错误检查,尤其是在打开文件方面。 Also you could take a look at what errno (consider using strerror ) provides.你也可以看看errno (考虑使用strerror )提供了什么。

A very good point brought up by user3121023 and Peter is the open mode: On Windows, there are some transformations done to the data you read from or write to a file if the file isn't opened in a so called binary mode. user3121023和 Peter 提出的一个很好的观点是打开模式:在 Windows 上,如果文件不是以所谓的二进制模式打开,则会对您读取或写入文件的数据进行一些转换。 This, for example, means that if your data contains a byte equal to '\\n' , then Windows will add an additional '\\r' (carriage return).例如,这意味着如果您的数据包含一个等于'\\n'的字节,那么 Windows将添加一个额外的'\\r' (回车)。

Firstly, you need to open the file for binary I/O (include a 'b' character in the mode string given to fopen() ).首先,您需要打开用于二进制 I/O 的文件(在提供给fopen()的模式字符串中包含一个'b'字符)。 Without that, the file is opened in text mode which, among other things, translates characters like newlines differently between systems.没有它,文件将以文本模式打开,除其他外,它会在系统之间以不同的方式转换换行符等字符。

Second, and more significant, is hinted by the fact you need to use sizeof() .其次,更重要的是,您需要使用sizeof()这一事实暗示了这一点。 The result yielded by sizeof is implementation defined, for anything for the char types which have size 1 by definition. sizeof产生的结果是实现定义的,对于根据定义大小为1char类型的任何内容。 sizeof int is implementation defined. sizeof int是实现定义的。 sizeof also yields different values for struct types, since there may be padding between members of different types, to meet alignment requirements for all the struct members. sizeof还为struct类型产生不同的值,因为不同类型的成员之间可能存在填充,以满足所有struct成员的对齐要求。 A consequence of these things being implementation defined, in the C standard, is that they vary between implementations (ie between compilers and host systems).在 C 标准中,这些东西是实现定义的结果是它们在实现之间(即在编译器和主机系统之间)有所不同。

That explains why you are getting different sizes between systems - different amounts of data are being written or read by each operation, on different systems.这解释了为什么您在系统之间获得不同的大小 - 在不同的系统上,每个操作正在写入或读取不同数量的数据。

To deal with those things, you either need to use techniques to ensure everything is the same size (which is often possible with care, but difficult because some of the properties of some types (like int and struct padding) can be changed by compilation options on some compilers) or do some marshalling (translate the struct types in memory to some consistent binary format for output)and unmarshelling (the reverse process).为了处理这些事情,你要么需要使用技术来确保所有的东西都是相同的大小(这通常是可能的,但很困难,因为某些类型的某些属性(如intstruct padding)可以通过编译选项进行更改在某些编译器上)或进行一些编组(将内存中的struct类型转换为某种一致的二进制格式以进行输出)和解组(相反的过程)。 The input process needs to be the reverse of the output process (suck in data from file as array of characters, and interpret the data to reconstruct your struct ).输入过程需要与输出过程相反(从文件中提取数据作为字符数组,并解释数据以重建您的struct )。

Even if two systems have the same sizes (for everything), there are concerns of how basic types (like int ) are represented in memory eg endianness.即使两个系统具有相同的大小(对于一切),也存在基本类型(如int )在内存中如何表示的问题,例如字节序。 That also needs to be handled with the method of input and output.这也需要用输入和输出的方法来处理。

One simpler way is to use text mode files and formatted I/O.一种更简单的方法是使用文本模式文件和格式化的 I/O。 That has the advantage of the files being (largely) transportable, but also means a larger file.这具有文件(主要)可传输的优点,但也意味着文件更大。

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

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