-1

我不明白程序中有什么错误。我正在定义一个指向结构数组的指针。Malloc 为它提供了足够的内存。初始化数组元素。然后使用 fwrite 将数组写入二进制文件。然后尝试读取相同的内容,回到另一个指向类似数组的指针,该数组有足够的内存分配给它。

#include<stdio.h>

typedef struct ss{
int *p;
char c;
double d;
char g;
float f;
} dd;

main(){

dd (*tt)[5];
int i=0,a[5]={4,1,6,9,3};
tt=malloc(sizeof(struct ss[5]));
for(i=0;i<5;i++){
   tt[i]->p=malloc(sizeof(int));
   tt[i]->p=&a[i];
   tt[i]->c=(char)('a'+i);
   tt[i]->d=(double)(5.234234+i);
   tt[i]->g=(char)('A'+i);
   tt[i]->f=(float)(15.234234+i);
}

FILE *F;
F=fopen("myfile","w+b");
size_t l;
l=fwrite(tt,sizeof(*tt),1,F);
fseek(F,0,SEEK_SET);
//printf("sizeof(dd)=%d   sizeof(*tt) =%d bytes written %d\n",sizeof(dd),sizeof(*tt),l);

dd (*xx)[5];

xx=malloc(sizeof(struct ss[5]));
l=fread(xx,sizeof(*xx),1,F);

for(i=0;i<5;i++){
printf("%d, %c,%f,%c,%f\n",*(xx[i]->p),xx[i]->c,xx[i]->d,xx[i]->g,xx[i]->f);
}
printf("Date Read %d \n",l);
for(i=0;i<5;i++){
free(xx[i]->p);
}
free(xx);
free(tt);
fclose(F);
remove("myfile");
}

输出:
4,a,5.234234,A,15.234234
分段错误

4

2 回答 2

1

您没有将数据写入您认为的位置,因为您访问tt不正确。您的错误访问是一致的,因此您可以读出第一条记录,但第二条记录与您认为的位置相去甚远——事实上,它被写入未初始化的内存并且从未保存过。尝试访问重新加载的数据显示了这一点。此外,您的结构中的 int* 无法按照您写出的方式正确写出,但这没有实际意义,因为您的程序的结构方式 - 如果您尝试在单独的运行中加载文件,那将是错误的的程序。fwrite并且fread不能跟随你的int*,因为它只是将您的结构视为一个位模式——它忠实地重建了您的指针,但现在您有一个指向随机内存块的指针,而您实际上并没有对其进行任何操作!但是,在这种情况下,您的指针仍然有效,因为您从未覆盖数据,但这是特定于在没有关闭程序的情况下写出文件、不刷新内存和读回它的场景——这不是文件写入的现实场景。还有另一个 StackOverflow 问题更详细地解释了这个错误。

无论如何,这是您如何访问内存的更大问题,删除了其他行:

dd (*tt)[5];
//...
tt=malloc(sizeof(struct ss[5]));
for(i=0;i<5;i++){
   tt[i]->p=malloc(sizeof(int));
   tt[i]->p=&a[i];
   //...
}

C 声明使用顺时针螺旋规则阅读,所以让我们看看我们所说的tt并将它与我们如何使用它进行比较。

tt是变量名。它的右边是一个右括号,所以我们继续处理当前范围。我们遇到 a *,然后是匹配的括号,然后是静态数组大小,然后是类型。使用顺时针螺旋规则,tt是指向数组(大小为 5)的指针dd。这意味着如果你取消引用 tt (使用(*tt)),你会得到一个dd[5],或者,如果你更喜欢这样想(C当然会),一个指向一个足够大的内存块开头的指针以容纳你的结构。更重要的是,这就是你所说的。C 实际上对指针类型并不是很挑剔,这就是为什么即使您犯了严重的类型错误,您的代码也会编译的原因。

您的 malloc 声明是正确的:它正在tt使用操作系统承诺的内存位置进行初始化,该内存位置有足够的空间供您使用 5 ss。因为 C 不关心数组大小边界检查等愚蠢的事情,所以一个 5 元素数组的struct ss大小可以保证是单个 的 5 倍struct ss,所以您实际上可以编写malloc(5 * sizeof(dd)),但是任何一种编写方式都可以。

但是让我们看看这里发生了什么:

tt[i]->p=malloc(sizeof(int));

哦哦。tt指向struct数组的指针dd,但您只是将其视为指向struct的指针数组dd

你想要什么:

  • 取消引用tt
  • i在指向的指针数组中找到第th 个元素dd
  • 前往现场p
  • 为它分配一个指向空间的指针

你实际上得到了什么:

  • i在指向数组的指针数组中找到第th 个元素dd
  • 取消引用它,将其视为指向 的指针dd,因为 C 不知道数组和指针之间的区别
  • 间接去现场p
  • 为它分配一个指向空间的指针

i为 0 时,这可以正常工作,因为数组中的第零个元素和数组本身位于同一位置。(数组没有标头,C _不理解数组和指针之间的区别,并允许您互换使用它们,这就是编译的原因。)

i不为 0 时,你会弄乱内存。现在您正在写入发生在您的指针之后的任何内存!它实际上是一个指针,但你告诉 C 它是一个数组,它相信你,在它的位置添加了 1 个元素宽度,并尝试执行所有这些操作。您在应该使用指针的地方使用数组,而在应该使用数组的地方使用指针。

您只写入为元素 0 分配的内存。除此之外,您正在写入不相关的内存,并且运气(在您的情况下,运气不好)使您的程序不会在那里崩溃。(如果有的话,你会更容易发现这是有罪的。)当你fwrite分配的内存时,第一个元素是有效的,其余的都是垃圾,你的fread结果是一个具有一个有效元素的数据结构,然后是随机堆垃圾,当您尝试取消引用指针时会导致崩溃(这仅是有效的,因为程序没有结束)。

这是访问指向数组的指针的正确方法:

(*tt)[i].p=malloc(sizeof(int));...

此外,您正在分配内存,然后立即忘记对它的唯一引用,这是内存泄漏,因为您正在使用对初始化所有内容的静态数组的引用覆盖指针。改用这个:

*((*tt)[i].p)=a[i]

我强烈建议您完整地学习A Tutorial on Pointers and Arrays。它将帮助您在将来避免此类问题。

请注意,xx在以完全相同的方式打印其内容时,您正在阅读错误。

于 2012-03-21T07:10:07.190 回答
0

你的指针用法不正确。在此代码段中:

dd (*xx)[5];

xx=malloc(sizeof(struct ss[5]));
l=fread(xx,sizeof(*xx),1,F);

for(i=0;i<5;i++){
printf("%d, %c,%f,%c,%f\n",*(xx[i]->p),xx[i]->c,xx[i]->d,xx[i]->g,xx[i]->f);
}

您将 xx 声明为指向 5 个“dd”结构的数组的指针。这就是它变得奇怪的地方。它是指向五个结构的指针,而不是五个结构的数组。

It would look something like this in memory:

dd[0] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]
dd[1] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]
...
dd[4] = [{p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}, {p, c, d, g, f}]

Instead of the intended:
dd[0] = {p, c, d, g, f}
dd[1] = {p, c, d, g, f}
...
dd[4] = {p, c, d, g, f}

当您从 0 迭代到 5 时,每个数组访问都会在内存中推进您的数组 sizeof(ss[5]) 字节而不是 sizeof(ss) 字节。取出多余的指针。

dd* xx;
xx = (dd*)malloc(sizeof(dd) * 5);
l = fread(xx, sizeof(dd), 5, F);

for(i = 0; i < 5; ++i) {
  printf("%d, %c, %f, %c, %f\n", xx[i].p, , xx[i].c, xx[i].d, xx[i].g, xx[i].f);
}

此外,您的结构有问题。如果要像这样直接写入磁盘,则不能包含指针。因此你的'int *p;' 成员需要改为'int p;'。否则,如果您从单独的应用程序中读取此文件,您存储的指针将不再指向整数,而是指向未分配的内存。

Writing application:
    int *p = 0x12345 ---> 5
0x12345 gets stored in the file for p.

Writing application reads the file.
    int *p = 0x12345 ---> 5
The pointer still points at the same memory because it is still the same memory
  layout.

New application reads the file.
    int *p = 0x12345 ---> ?????
The pointer doesn't point to a known piece of memory because the memory layout
  has changed in this new instance of the application. This could crash or
  cause a security issue.
于 2012-03-21T06:57:29.943 回答