1

重要な編集:

申し訳ありませんが、私は構造に大きな間違いを犯しました。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?*/

次に、ファイル ポインタ fp を指定して fwrite... を使用します。

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

ここでファイルを閉じ、後で開いて構造を読み込みます。問題は次のとおりです。構造体を読み取るとき、格納されている変数も読み取っているのでしょうか。

次に、次のようなことができますか。

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

それとも、構造だけでなく何かを fread する必要がありますか、または何らかの方法でそれらを割り当てる必要がありますか? fread を次のように仮定します。

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

また、上記のように、現在の関数で構造を定義したと仮定します。

助けてくれてありがとう!

4

5 に答える 5

3

ここに問題があります:

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

AttenceList は名前を として保存するためchar *、実際のテキストではなく、ポインターを書き出すだけです。それを読み返すと、ポインタが参照しているメモリにはおそらく別のものが含まれているでしょう。

次の 2 つの選択肢があります。

  1. 文字配列( char names[MAXSIZE])をattendenceListに入れます。
  2. 生のデータ構造を書くのではなく、必要なフィールドを書きます。
于 2010-03-04T20:31:26.860 に答える
1

メンバーを含む構造体のメモリ レイアウトを記述しています。

構造を再度読み込むと、それらを元に戻すことができます。少なくとも、同じコンパイラとコンパイラ設定でコンパイルされたプログラムを使用して、同じプラットフォームでそれを行う場合。

nameメンバーは char として宣言されているため、文字列を格納することはできません。

name次のようなポインターの場合:

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

構造体をファイルに読み書きするべきではありません。メモリに配置されているように構造体を記述します。これには、nameポインターの場合の値が含まれます。

fwrite は、構造内のポインターについて何も知らないため、ポインターを追跡せず、ポインターが指すものを書き込みません。

構造体を再度読み取ると、ポインタのアドレスが読み取られますが、それは意味のnameあるものを指していない可能性があります。

配列として宣言する場合nameは、配列とその内容が構造の一部であるため、問題ありません。

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

nameいつものように、文字列 (ヌル ターミネータを含む) を32 より大きい値にコピーしようとしないでください。あなたのstruct.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

あなたが尋ねる:

ここでファイルを閉じ、後で開いて構造を読み込みます。問題は次のとおりです。構造体を読み取るとき、格納されている変数も読み取っているのでしょうか。

いいえ、sizeof(names) はコンパイル時に定義される定数値です。と同じになります

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

何を指すかのサイズは含まれず、ポインター自体のサイズのみが含まれますnames.name

したがって、これをファイルに書き込むときに 2 つの問題があります。

  1. 実際に名前文字列をファイルに書き込んでいるわけではありません
  2. ファイルにポインター値を書き込んでいますが、それを読み戻しても意味がありません。

あなたのコードは現在書かれているので、名前を読み返すnames.nameと、どこかを指しますが、「John Doe\0」を指しません。

あなたがする必要があるnames.nameのは、ポインター値の代わりにが指す文字列を書くことです。

あなたがする必要があるのは、構造の「フラット化」と呼ばれることがあります。ポインタを含まないが、使用したい構造と同じデータを保持する構造をメモリ内に作成し、フラット化された構造をディスクに書き込みます。これはそのための 1 つの方法です。

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* nameの代わりに意味したと思いますchar name。また、char配列の長さではなくsizeof(name)サイズを取得しているため、4が返されます。したがって、あなたの の中にnotchar*と書くべきです。strlen(name)sizeof(name)fwrite

上記の例では、null 終端なしで文字列の正確なサイズを保存することをお勧めします。後で取得できるため、文字列の長さを保存する必要はありません。

ファイルから文字列だけを読み取っていて、null 終端なしで正確なサイズを書き込んだ場合。次に、データを読み込んだ後、バッファを手動で null 終端する必要があります。そのため、少なくとも読み込んでいるデータのサイズにプラス 1 を割り当てるようにしてください。
その後、その配列の最後のバイトを に設定できます'\0'

一度に構造体全体をバッファーに書き込む場合は、パディングのために注意する必要があります。パディングは常に同じとは限りません。

構造体を読み取るとき、格納されている変数も読み取っていますか?

はい、そうですが、問題は、上で述べたように、実際の char 配列ではなく、ポインター char* (4 バイト) を格納することです。構造体要素を個別に保存することをお勧めします。

于 2010-03-04T20:22:39.007 に答える
0

いくつかのこと。

構造体は単なるメモリの塊です。たくさんのバイトを取り、それらに境界を描いているだけです。構造要素へのアクセスは、特定のタイプのデータとしてキャストされた特定のメモリ オフセットを取得する便利な方法です。

文字列を型に割り当てようとしていますchar。これは機能しません。C では、文字列は末尾に NULL バイトを持つ文字の配列です。これを機能させる最も簡単な方法は、サイドに名前の固定バッファーを設定することです。構造体を作成するときは、名前をバッファーにコピーする必要があります (バッファーに含まれるバイト数を超えないように注意してください)。その後、ファイルからバッファを 1 ステップで読み書きできます。

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

別の方法として、名前を文字列へのポインタにすることもできます。ファイルとの間で構造体を読み書きするには、名前がメモリ内の別の場所に保存されていることを考慮する必要があるため、これにより、実行しようとしていることがより複雑になります。これは、2 回の書き込みと 2 回の読み取り (方法によって異なります) とname、名前のデータを読み取る場所にポインターを正しく割り当てることを意味します。

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

構造体の最後に長さゼロの配列を使用するトリックを使用して、動的にサイズ設定された構造体を使用して、それを行うことができる 3 番目の方法があります。名前の長さが分かれば、正しい量 (sizeof(structattendList) + 文字列の長さ) を割り当てます。次に、それを 1 つの連続したバッファーに入れます。sizeof(structattendList) は、書き込み/読み取りに必要なサイズではないことを覚えておく必要があります。これは、最初は少し混乱するかもしれません。これは、すべてのコンパイラでサポートされていない一種のハックでもあります。

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