阅读和写作结构[C]

时间:2010-03-04 20:21:05

标签: c data-structures fread structure

重要编辑:

对不起大家,我在结构上犯了一个大错。 char * name;意味着在结构之外,在结构之后写入文件。 这样,你读取结构,找出名称的大小,然后读入字符串。还解释了为什么不需要空终止符。 但是,我感觉到某个地方,我的实际问题已得到解答。如果有人想编辑他们的回复,那么我可以选择一个最合适的回复。

同样,我问的问题是“如果你在一个结构中阅读,你是否也在阅读它所拥有的数据,或者你需要以其他方式访问它”。

对不起有困惑

对于一项任务,我一直负责一个程序,它将结构写入和读取到磁盘上(使用fread和fwrite)。

我无法理解这个概念。 可以说我们有这样的结构:

typedef struct {
    short nameLength;
    char* name;
}attendenceList;

attendenceList names;

现在假设我们给它这个数据:

names.name = "John Doe\0";
names.nameLength = strlen(names.name); /*potentially -1?*/

然后我们使用fwrite ...给出一个文件指针fp。

fwrite(&names,sizeof(names),1,fp);

现在我们关闭文件,稍后打开它以读取结构。 问题是这样的:当我们读到结构时,我们是否也在阅读它存储的变量?

我们现在可以做类似的事情:

if(names.nameLength < 10)
{
 ...
}

或者我们是否需要更多地了解结构,或以某种方式分配它们? 假设fread是:

fread(&names,sizeof(names),1,fp);

同样假设我们已经在当前函数中定义了结构,如上所述。

感谢您的帮助!

5 个答案:

答案 0 :(得分:3)

你有问题:

fwrite(&names,sizeof(names),1,fp);

由于attendenceList将名称保存为char *,因此只会写出指针,而不是实际文本。当你再读回来时,指针引用的内存很可能还有其他内容。

您有两种选择:

  1. 将一个字符数组(char names[MAXSIZE])放在attendenceList中。
  2. 不要编写原始数据结构,而是编写必要的字段。

答案 1 :(得分:1)

您正在编写结构的内存布局,其中包括其成员。

如果再次阅读结构,你会得到它们 - 至少如果你在同一平台上进行,使用相同的编译器和编译器设置编译程序。

您的name成员被声明为char,因此您无法在其中存储字符串。

如果name是这样的指针:

typedef struct {
    short nameLength;
    char *name;
}attendenceList;

你真的不应该将结构读/写到文件中。您将在内存中编写结构,并包含name指针的值。

fwrite对结构中的指针一无所知,它不会跟随指针,也会写出他们指向的任何内容。

当你再次读回结构时,你会读到name指针中的地址,这可能不再指向任何合理的东西了。

如果您将name声明为数组,那么您将没问题,因为数组及其内容是结构的一部分。

typedef struct {
    short nameLength;
    char name[32];
}attendenceList;

与往常一样,请确保您不要尝试将字符串(包括其nul终结符)复制到大于32的name。当您再次阅读它时。设置yourstruct.name [31] = 0;所以你确定缓冲区是空终止的。

要编写结构,您可以

attendenceList my_list;

//initialize my_list
if(fwrite(&my_list,sizeof my_list,1,f) != 1) {
 //handle error
}

再次阅读:

attendenceList my_list;

//initialize my_list
if(fread(&my_list,sizeof my_list,1,f) != 1) {
 //handle error
}

}

答案 2 :(得分:0)

我假设您的意思是char* name而不是char name。 同样sizeof(name)将返回4,因为您获得char*的大小而不是char数组的长度。因此,您应该在strlen(name)内写sizeof(name)而不是fwrite

在上面的示例中,我建议存储字符串精确大小而不使用null终止。您不需要存储字符串长度,因为您可以在之后获得。

如果您只是从文件中读取一个字符串,并且您编写了没有空终止的确切大小。然后,在读取数据后,需要手动将null终止缓冲区。 因此,请确保至少分配您正在阅读的数据的大小加上1 然后,您可以将该数组的最后一个字节设置为'\0'

如果你一次写一个完整的结构到缓冲区,你应该小心因为填充。填充可能并不总是相同。

  

当我们读到结构时,我们是否也在读它存储的变量?

是的,但你遇到的问题是,如上所述,你将存储指针char *(4个字节)而不是实际的char数组。我建议单独存储struct元素。

答案 3 :(得分:0)

你问:

  

现在我们关闭文件,稍后打开它以读取结构。问题是这样的:当我们读到结构时,我们是否也在读它存储的变量?

没有。 sizeof(names)是在编译时定义的常量值。它将与

相同
sizeof(short) + sizeof(void*) + some_amount_of_padding_to_align_things

NOT 包含names.name指向的大小,它只包含指针本身的大小。

因此,将此文件写入文件时会遇到两个问题。

  1. 您实际上并没有将名称字符串写入文件
  2. 您正在为该文件写一个指针值,当您将其读回时没有任何意义。
  3. 由于您的代码当前已写入,当您回读这些名称时,names.name将指向某处,但它不会指向“John Doe \ 0”。

    您需要做的是编写names.name指向的字符串而不是指针值。

    你需要做的事情有时被称为“展平”结构,你在内存中创建一个不包含指针的结构,但是保存与你想要使用的结构相同的数据,然后你将扁平结构写入磁盘。这是一种方法。

    typedef struct {
        short nameLength;
        char  name[1]; // this will be variable sized at runtime.
    }attendenceListFlat;
    
    int cbFlat = sizeof(attendenceListFlat) + strlen(names.name);
    attendenceListFlat * pflat = malloc(cbFlat);
    pflat->nameLength = names.nameLength;
    strcpy(pflat->name, names.name);
    
    fwrite(pflat, cbFlat, 1, fp);
    

    扁平化结构以最小大小为1的数组结束,但是当我们使用malloc时,我们添加了strlen(names.name),因此我们可以将其视为strlen(names.name)+1大小的数组。

答案 4 :(得分:0)

一些事情。

结构只是大块的记忆。它只需要一堆字节并在它们上绘制边界。访问结构元素只是将特定内存偏移量转换为特定类型数据的便捷方式

您正在尝试将字符串分配给char类型。这不行。在C中,字符串是字符数组,在它们的末尾有一个NULL字节。让它工作的最简单方法是为名称设置一个固定缓冲区。在创建结构时,您必须将名称复制到缓冲区中(非常小心,不要写入比缓冲区包含的更多的字节)。然后,您可以一步写入/读取文件中的缓冲区。

struct attendanceList {
     int  namelen;
     char name[256]; //fixed size buffer for name
}

另一种方法是将名称作为指针添加到字符串中。这使得您尝试执行的操作变得更加复杂,因为为了向/从文件写入/读取结构,您必须考虑到名称存储在内存中的不同位置。这意味着两次写入和两次读取(取决于您的操作方式)以及正确地将name指针分配给您读取名称数据的位置。

struct attendanceList {
    int   namelen;
    char* name; //the * means "this is a pointer to a char somewhere else in memory"
}

你可以采用第三种方式,使用一个动态大小的结构,使用一个结构末尾带有零长度数组的技巧。一旦知道名称有多长,就可以分配正确的金额(sizeof(struct attendanceList)+ string的长度)。然后你将它放在一个连续的缓冲区中。您只需要记住sizeof(struct attendanceList)不是您需要写/读的大小。作为一个开端,这可能有点令人困惑。它也是一种在所有编译器都不支持的黑客攻击。

struct attendanceList {
   int namelen;
   char name[0];  //this just allows easy access to the data following the struct.  Be careful!

}