读写结构 [C]

发布于 2024-08-24 15:50:31 字数 1011 浏览 7 评论 0原文

重要编辑:

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

同样,我问的问题是“如果您读取结构,您是否也读取它所保存的数据,或者您是否需要以其他方式访问它”。

抱歉造成混乱

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

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

感谢您的帮助!

IMPORTANT EDIT:

Sorry everyone, i made a big mistake in the structure.
char *name; is meant to be outside of the structure, written to the file after the structure.
This way, you read the structure, find out the size of the name, then read in the string. Also explains why there is no need for a null terminator.
However, i feel somewhere, my actual question has been answered. If someone would like to edit their responses so i can choose one which is the best fitting i'd appreciate it.

Again, the question I was asking is "If you read in a structure, are you also reading in the data it holds, or do you need to access it some other way".

Sorry for the confusion

For an assignment, I've been tasked with a program which writes and reads structures to a disk (using fread and fwrite).

I'm having trouble grasping the concept.
Lets say we have this structure:

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

attendenceList names;

now assume we give it this data:

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

and then we use fwrite... given a file pointer fp.

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

now we close the file, and open it later to read in the structure.
the question is this: when we read in the structure, are we also reading in the variables it stores?

Can we then now do something like:

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

Or do we have to fread something more then just the structure, or assign them somehow?
Assuming the fread is:

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

Also assuming we've defined the structure in our current function, as above.

Thanks for the help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

我的影子我的梦 2024-08-31 15:50:31

这里有一个问题:

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

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

您有两种选择:

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

You have a problem here:

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

Since attendenceList saves the name as a char * this will just write out the pointer, not the actual text. When you read that back in, the memory the pointer is referencing will most likely have something else in it.

You have two choices:

  1. Put a character array (char names[MAXSIZE]) in attendenceList.
  2. Don't write the raw data structure, but write the necessary fields.
陈甜 2024-08-31 15:50:31

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

如果您再次读回结构,您将得到它们 - 至少如果您在同一平台上使用相同的编译器和编译器设置编译的程序执行此操作。

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

如果 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
}

}

You're writing the memory layout of the structure, which includes its members.

You'll get them back if you read the structure back in again - atleast if you do it on the same platform, with a program compiled with the same compiler and compiler settings.

Your name member is declared just as a char, so you can't store a string in it.

If name was a pointer like this:

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

You really should not read/write the struct to a file. You will write the structure as it's laid out in memory, and that includes the value if the name pointer.

fwrite knows nothing about pointers inside your structure, it will not follow pointers and also write whatever they point to.

when you read the structure back again, you'll read in the address in the name pointer, and that might not point to anything sensible anymore.

If you declare name as an array, you'll be ok, as the array and its content is part of the structure.

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

As always, make sure you don't try to copy a string - including its nul terminator- to name that's larger than 32. And when you read it back again. set yourstruct.name[31] = 0; so you are sure the buffer is null terminated.

To write a structure, you'd do

attendenceList my_list;

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

And to read it back again:

attendenceList my_list;

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

}

流心雨 2024-08-31 15:50:31

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

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

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

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

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

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

I'm assuming you meant char* name instead of char name.
Also sizeof(name) will return 4 because you are getting the size of a char* not the length of the char array. So you should write strlen(name) not sizeof(name) inside your fwrite.

In your above example I would recommend storing the string exact size without the null termination. You don't need to store the string length as you can get that after.

If you are reading just a string from a file, and you wrote the exact size without the null termination. Then you need to manually null terminate your buffer after you read the data in.
So make sure you allocate at least the size of your data you are reading in plus 1.
Then you can set the last byte of that array to '\0'.

If you write a whole struct at a time to the buffer, you should be careful because of padding. The padding may not always be the same.

when we read in the structure, are we also reading in the variables it stores?

Yes you are, but the problem you have is that as I mentioned above you will be storing the pointer char* (4 bytes) and not the actual char array. I would recommend storing the struct elements individually.

疾风者 2024-08-31 15:50:31

你问:

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

否。 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 大小的数组。

You ask:

now we close the file, and open it later to read in the structure. the question is this: when we read in the structure, are we also reading in the variables it stores?

No. sizeof(names) is a constant value defined at compile time. It will be the same as

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

it will NOT include the size of what names.name points to, it will only include the size of the pointer itself.

So you have two problems when writing this to a file.

  1. you aren't actually writing the name string to the file
  2. you are writing a pointer value to the file that will have no meaning when you read it back.

As your code is currently written, When you read back the names, names.name will point to somewhere, but it won't point to "John Doe\0".

What you need to do is to write the string pointed to by names.name instead of the pointer value.

What you need to do is sometimes called "flattening" the structure, You make a structure in memory that contains no pointers, but holds the same data as the structure you want to use, then you write the flattened structure to disk. This is one way to do that.

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);

The flattened structure ends with an array that has a minimum size of 1, but when we malloc, we add strlen(names.name) so we can treat that as an array of strlen(names.name)+1 size.

三五鸿雁 2024-08-31 15:50:31

有几件事。

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

您正尝试将字符串分配给 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 attendList) + 字符串长度)。然后你将它放在一个连续的缓冲区中。您只需要记住 sizeof(struct attendList) 不是您需要写入/读取的大小。作为开始,这可能有点令人困惑。这也是一种并非所有编译器都支持的 hack。

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

}

A few things.

Structures are just chunks of memory. It's just taking a bunch of bytes and drawing boundaries on them. Accessing structure elements is just a convenient way of getting a particular memory offset cast as a particular type of data

You are attempting to assign a string to a char type. This will not work. In C, strings are arrays of characters with a NULL byte at the end of them. The easiest way to get this to work is to set a side a fixed buffer for the name. When you create your structure you'll have to copy the name into the buffer (being very careful not to write more bytes than the buffer contains). You can then write/read the buffer from the file in one step.

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

Another way you could do it is by having the name be a pointer to a string. This makes what you're trying to do more complicated, because in order to write/read the struct to/from a file, you will have to take into account that the name is stored in a different place in memory. This means two writes and two reads (depending on how you do it) as well as correctly assigning the name pointer to wherever you read the data for the name.

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

There's a third way you could do it, with a dynamically sized struct using a trick with a zero length array at the end of a struct. Once you know how long the name is, you allocate the correct amount (sizeof(struct attendanceList) + length of string). Then you have it in one contiguous buffer. You just need to remember that sizeof(struct attendanceList) is not the size you need to write/read. This might be a little confusing as a beginning. It is also kind of a hack that's not supported under all compilers.

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

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文