1

重要编辑:

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

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

对困惑感到抱歉

对于一项任务,我的任务是编写一个程序,该程序将结构写入和读取磁盘(使用 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);

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

谢谢您的帮助!

4

5 回答 5

3

你这里有问题:

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

由于attenceList 将名称保存为a,char *这只会写出指针,而不是实际文本。当你读回它时,指针所引用的内存很可能还有其他东西。

你有两个选择:

  1. 在attenceList 中放入一个字符数组( char names[MAXSIZE])。
  2. 不要写原始数据结构,而是写必要的字段。
于 2010-03-04T20:31:26.860 回答
1

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

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

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

如果name是这样的指针:

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

您真的不应该将结构读/写到文件中。您将按照它在内存中的布局编写结构,并且如果是name指针,则包括该值。

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

当你再次读回结构时,你会读入name指针中的地址,这可能不再指向任何有意义的东西。

如果你声明name为一个数组,你会没事的,因为数组及其内容是结构的一部分。

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

与往常一样,请确保您不要尝试将字符串(包括其 nul 终止符)复制到name大于 32 的字符串。当您再次读取它时。设置 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
}

}

于 2010-03-04T20:31:49.643 回答
0

我假设你的意思是char* name而不是char name. 也sizeof(name)将返回 4 ,因为您获得的是 a 的大小而char*不是 char 数组的长度。所以你不应该strlen(name)sizeof(name)你的fwrite.

在您上面的示例中,我建议在不使用空终止的情况下存储字符串的确切大小。您不需要存储字符串长度,因为您可以在之后得到它。

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

如果您一次将整个结构写入缓冲区,则由于填充,您应该小心。填充可能并不总是相同的。

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

是的,但是您遇到的问题是,正如我上面提到的,您将存储指针 char*(4 个字节)而不是实际的 char 数组。我建议单独存储结构元素。

于 2010-03-04T20:22:39.007 回答
0

你问:

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

编号 sizeof(names) 是在编译时定义的常量值。这将与

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

不会包括names.name指向的大小,它只会包括指针本身的大小。

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

  1. 您实际上并没有将名称字符串写入文件
  2. 您正在向文件写入一个指针值,当您读回它时将没有任何意义。

由于您当前编写的代码,当您读回名称时,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 大小的数组。

于 2010-03-04T20:33:23.323 回答
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 admissionList) + 字符串长度)。然后你把它放在一个连续的缓冲区中。您只需要记住 sizeof(struct admissionList) 不是您需要写入/读取的大小。作为一个开始,这可能有点令人困惑。这也是一种并非所有编译器都支持的 hack。

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

}

于 2010-03-04T20:44:17.203 回答