-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間違ってアクセスしていたため、自分がいると思っていた場所にデータを書き込んでいませんでした。誤ったアクセスは一貫していたので、最初のレコードを読み取ることができましたが、2 番目のレコードは思っていた場所とはかけ離れていました。実際には、初期化されていないメモリに書き込まれ、保存されていませんでした。リロードされたデータにアクセスしようとすると、これが示されます。さらに、構造体の 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 の宣言はThe Clockwise Spiral Rulettで読み取られるため、これまでに述べたことを見て、それをどのように使用しているかを比較してみましょう。

tt変数名です。その右側には閉じ括弧があるため、現在のスコープを処理し続けます。*、次に対応する括弧、静的配列サイズ、型に遭遇します。時計回りの螺旋規則を使用すると、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 の配列へのポインターですが、 structポインターの配列ddとして扱っただけです。dd

あなたが欲しかったもの:

  • 逆参照tt
  • iへのポインタの配列で th 要素を見つけるdd
  • フィールドに移動p
  • int のスペースへのポインターを割り当てます

実際に得たもの:

  • iの配列へのポインターの配列で th 要素を検索します。dd
  • C は配列とポインタの違いを認識していないため、これをへのポインタとして扱い、逆参照します。dd
  • 間接的にフィールドに移動p
  • int のスペースへのポインターを割り当てます

が 0 の場合i、配列の 0 番目の要素と配列自体が同じ場所にあるため、これは適切に機能します。(配列にはヘッダーがなく、C _does は配列とポインターの違いを理解せず、それらを同じ意味で使用できるようにします。そのため、これはまったくコンパイルされません。)

が 0 でない場合i、膨大なメモリの混乱が生じます。これで、たまたまポインタをたどったメモリに書き込んでいます。これは実際にはポインターですが、配列であると C に伝えると、C はあなたを信じて、その位置に要素幅を 1 つ追加し、これらすべての操作を実行しようとしました。ポインターを使用する必要がある場所で正確に配列を使用しており、配列を使用する必要がある場所で正確にポインターを使用しています。

要素 0 に割り当てたメモリにのみ書き込みます。それを超えると、無関係なメモリに書き込みます。プログラムがすぐにクラッシュしないようにしたのは運 (あなたの場合は不運) です。(もしあれば、これを有罪の行として見つけるのは簡単だったでしょう。)fwriteメモリを割り当てたとき、最初の要素は有効で、残りはガベージであり、fread1 つの有効な要素を持つデータ構造になります。 、次にランダム ヒープ ガベージが発生し、ポインターを逆参照しようとしたときにクラッシュが発生しました (これは、プログラムが終了しなかったためにのみ有効です)。

配列へのポインタにアクセスする正しい方法は次のとおりです。

(*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'構造体の配列へのポインターとして宣言しています。これはそれが奇妙になるところです。これは5つの構造体へのポインタであり、5つの構造体の配列ではありません。

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)バイトではなく、メモリ内の配列sizeof(ss [5])バイトを進めています。余分なポインタを取り出します。

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;' 代わりに、メンバーは'intp;'である必要があります。そうしないと、別のアプリケーションからこのファイルを読み取る場合、保存したポインタは整数を指すのではなく、割り当てられていないメモリを指します。

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 に答える