5

私は学校の課題に次のコードブロックを使用する必要があります。厳密には変更を加えません。

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}* pStudentRecord;

pStudentRecord* g_ppRecords;
int g_numRecords =0;

ここで、g_ppRecordsは、構造体へのポインターの配列であると想定されています。私が完全に理解していないのは、配列は次のように定義する必要があるため、ステートメントが配列であることをどのようにpStudentRecords *g_ppRecords;意味するのかということです。g_ppRecords

type arrayname[size];

g_ppRecordsに動的にメモリを割り当てようとしましたが、それは役に立ちません。

g_ppRecords = (pStudentRecord*) malloc(sizeof(pStudentRecord*)*(g_numRecords+1));
4

4 に答える 4

3

編集:「BIGMISTAKE」セクションを更新しました。

Cスタイル(C ++とは異なります!)のtypedefに関する簡単なレッスン、およびその理由と使用方法。

まず、基本的なtypedefのトリックです。

typedef int* int_pointer;
int_pointer ip1;
int *ip2;
int a;    // Just a variable
ip1 = &a; // Sets the pointer to a
ip2 = &a; // Sets the pointer to a
*ip1 = 4; // Sets a to 4
*ip2 = 4; // Sets a to 4

ip1とip2は同じ型です。つまり、ip1の宣言に*を入れていなくても、type-in​​tへのポインターです。その*は代わりに宣言に含まれていました。

トピックの切り替え。あなたは配列を次のように宣言することについて話します

int array1[4];

実行時にこれを動的に行うには、次のようにします。

int *array2 = malloc(sizeof(int) * 4);
int a = 4;
array1[0] = a;
array2[0] = a; // The [] implicitly dereferences the pointer

では、ポインタの配列が必要な場合はどうでしょうか。次のようになります。

int *array1[4];
int a;
array1[0] = &a; // Sets array[0] to point to variable a
*array1[0] = 4; // Sets a to 4

その配列を動的に割り当てましょう。

int **array2 = malloc(sizeof(int *) * 4);
array2[0] = &a; // [] implicitly dereferences
*array2[0] = 4; // Sets a to 4

int**に注意してください。これは、pointer-topointer-to-intを意味します。必要に応じて、ポインターtypedefを使用できます。

typedef int* array_of_ints;
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4);
array3[0] = &a; // [] implicitly dereferences
*array3[0] = 4; // Sets a to 4

その最後の宣言に*が1つしかないことを確認してください。これは、そのうちの1つが「typedef内」にあるためです。最後の宣言で、int(int *)への4つのポインターで構成されるサイズ4の配列ができました。

ここで演算子の優先順位を指摘することが重要です。間接参照演算子[]は、*演算子よりも優先されます。完全に明確にするために、私たちが行っているのはこれです:

*(array3[0]) = 4;

それでは、トピックをstructsとtypedefsに変更しましょう。

struct foo { int a; }; // Declares a struct named foo
typedef struct { int a; } bar; // Typedefs an "ANONYMOUS STRUCTURE" referred to by 'bar'

なぜ匿名の構造体をtypedefするのですか?さて、読みやすさのために!

struct foo a; // Declares a variable a of type struct foo
bar b;        // Notice how you don't have to put 'struct' first

関数を宣言しています...

funca(struct foo* arg1, bar *arg2);

arg2の前に「struct」を配置する必要がなかった方法をご覧ください。

これで、使用する必要のあるコードがこの方法で構造を定義していることがわかります。

typedef struct { } * foo_pointers;

これは、以前にポインタの配列を実行した方法に類似しています。

typedef int* array_of_ints;

並べて比較する

typedef struct { } * foo_pointers;
typedef int* array_of_ints;

唯一の違いは、1つは構造体{}に対するものであり、もう1つはintに対するものであるということです。

foo_pointersを使用すると、fooへのポインターの配列を次のように宣言できます。

foo_pointers fooptrs[4];

これで、アクセスできない匿名構造への4つのポインターを格納する配列ができました。

トピックスイッチ!

残念ながら、あなたの先生は間違いを犯しました。上記のfoo_pointers型のsizeof()を見ると、構造体のサイズではなく、その構造体へのポインターのサイズが返されていることがわかります。これは、32ビットプラットフォームの場合は4バイト、64ビットプラットフォームの場合は8バイトです。これは、構造体自体ではなく、POINTER TOASTRUCTをtypedefしたためです。sizeof(pStudentRecord)は4を返します。

したがって、構造自体に明らかな方法でスペースを割り当てることはできません。ただし、コンパイラはこの愚かさを考慮に入れています。pStudentRecordは、メモリを有効に割り当てるために使用できる名前/タイプではなく、匿名の「概念」構造へのポインタですが、そのサイズをコンパイラにフィードできます。

pStudnetRecord g_ppRecords [2]; pStudentRecord * record = malloc(sizeof(* g_ppRecords [1]));

より良い方法はこれを行うことです:

typedef struct { ... } StudentRecord;  // Struct
typedef StudentRecord* pStudentRecord; // Pointer-to struct

これで、構造体StudentRecordと、pStudentRecordを使用したそれらへのポインターを明確な方法で作成できるようになりました。

強制的に使用する方法は非常に悪い習慣ですが、現時点では必ずしも問題ではありません。intsを使用した単純化された例に戻りましょう。

私の人生を複雑にするためにtypedefを作りたいが、ここで起こっている概念を説明したい場合はどうなりますか?古いintコードに戻りましょう。

typedef int* array_of_ints;
int *array1[4];
int **array2 = malloc(sizeof(int *) * 4); // Equivalent-ish to the line above
array_of_ints *array3 = malloc(sizeof(array_of_ints) * 4);
int a, b, c, d;
*array1[0] = &a; *array1[1] = &b; *array1[2] = &c; *array1[3] = &d;
*array2[0] = &a; *array2[1] = &b; *array2[2] = &c; *array2[3] = &d;
*array3[0] = &a; *array3[1] = &b; *array3[2] = &c; *array3[3] = &d;

ご覧のとおり、これはpStudentRecordで使用できます。

pStudentRecord array1[4];
pStudentRecord *array2 = malloc(sizeof(pStudentRecord) * 4);

すべてをまとめると、論理的に次のようになります。

array1[0]->firstName = "Christopher";
*array2[0]->firstName = "Christopher";

同等です。(注:上記のように正確に実行しないでください。実行時に文字列にchar *ポインターを割り当てることは、十分なスペースがすでに割り当てられていることがわかっている場合にのみ問題ありません)。

これは本当に最後の1ビットをもたらすだけです。私たちがmallocしたこのすべての記憶をどうしますか?どうすれば解放できますか?

free(array1);
free(array2);

そして、ポインター、匿名構造体のtypedef、およびその他のものに関する深夜のレッスンの終わりがあります。

于 2012-09-15T04:48:27.273 に答える
2

pStudentRecord構造体へのポインタとしてtypedefされていることを確認してください。Cのポインタは、そのブロックに1つの要素(通常の「スカラー」ポインタ)または10の要素(「配列」ポインタ)が含まれているかどうかに関係なく、メモリブロックの先頭を指すだけです。したがって、たとえば、次の

char c = 'x';
char *pc = &c;

pc文字で始まるメモリの一部を指し示しますが、'x'次のようになります

char *s = "abcd";

sで始まり"abcd"(その後にヌルバイトが続く)メモリの一部を指します。タイプは同じですが、異なる目的で使用される場合があります。

したがって、割り当てられると、g_ppRecordsたとえばを実行することでの要素にアクセスできますg_ppRecords[1]->firstName

ここで、この配列を割り当てるには、次のようにしますg_ppRecords = malloc(sizeof(pStudentRecord)*(g_numRecords+1));(ただし、sizeof(pStudentRecord*)sizeof(pStudentRecord)は両方ともポインター型であるため、等しいことに注意してください)。これにより、構造体ポインタの初期化されていない配列が作成されます。配列内の構造体ポインターごとに、新しい構造体を割り当てて値を指定する必要があります。問題の核心は、単一の構造をどのように割り当てるかです。

g_ppRecords[1] = malloc(/* what goes here? */);

sizeof幸いなことに、実際には:でポインタを逆参照できます。

g_ppRecords[1] = malloc(sizeof(*g_ppRecords[1]));

sizeofこれはコンパイラ構造であることに注意してください。g_ppRecords[1]が有効なポインタでなくても、は有効であるため、コンパイラは正しいサイズを計算します。

于 2012-09-15T03:42:28.873 に答える
0

配列は、多くの場合、最初の要素へのポインターで参照されます。10人の学生レコードに十分なスペースをmallocし、そのスペースの先頭へのポインターをg_ppRecordsに格納すると、g_ppRecords [9]は9レコードポインターの長さを前方にカウントし、そこにあるものを逆参照します。スペースを正しく管理した場合、10の十分なスペースを予約したため、アレイの最後のレコードは何になりますか。

つまり、スペースを割り当てたので、配列としても含めて、適切な長さであれば、好きなように扱うことができます。

g_numRecords+1レコードにスペースを割り当てている理由がわかりません。g_numRecordsの名前が紛らわしい場合を除いて、配列内に必要以上に1つ多くのスペースがあります。

于 2012-09-15T03:50:47.313 に答える
-1

ここで、g_ppRecordsは、構造体へのポインターの配列であると想定されています。私が完全に理解していないのは、ステートメント* pStudentRecords g_ppRecords; g_ppRecordsが配列であることを意味します。配列は次のように定義する必要があります

タイプarrayname[size];

umm type arrayname[size];は、Cで配列を定義する多くの方法1つです。

これは配列を静的に定義し、ほとんどの値はその定義の場所に応じてスタックに格納されます。配列のサイズはコンパイル時に知る必要がありますが、最近のコンパイラではそうではない場合があります。

もう1つの方法は、実行時に配列を動的に作成することです。したがって、コンパイル時にサイズを知る必要はありません。ここでポインターが入力されます。ポインターは、動的に割り当てられたメモリチャンクのアドレスを格納する変数です。

type *array = malloc(sizeof(type) * number_of_items);簡単な例は、このmallocがに格納されているメモリアドレスを返すようなものarrayです。安全上の理由から、戻り型を型キャストしないことに注意してください。

手元の問題に戻ります。

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}*  pStudentRecord;

pStudentRecord* g_ppRecords;
int g_numRecords = 0;

これtypedefは、ほとんどの場合とは少し異なります。}*基本的には構造体へのポインタなので、次のようになります。

pStudentRecord* g_ppRecords;

実際には:

struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
}** pStudentRecord;

なぜ彼らがtypedefこのように定義するのか、それは私を超えているのか、そして私は個人的にそれをお勧めしません、なぜですか?

問題の1つは、名前から構造体のサイズを取得する方法です。単純なことはできません!使用する場合は、基礎となるアーキテクチャsizeof(pStudentRecord)を取得する4か、それ8に依存します。これは、構造のサイズがわからない場合、typedef名を使用して実際に動的に割り当てることができないため、ポインタであるため、次のような2番目の構造体を宣言します。このように:

typedef struct 
{
    char* firstName;
    char* lastName;
    int id;
    float mark;
} StudentRecord;

g_ppRecords = malloc(sizeof(StudentRecord) * g_numRecords);

いずれにせよ、あなたは本当にこのコードを最初に作成した人、またはあなたの懸念を維持し提起する人に連絡する必要があります。

g_ppRecords=(pStudentRecord) malloc( (sizeof(char*) + 
                                  sizeof(char*) + 
                                  sizeof(int)   + 
                                  sizeof(float)) *(g_numRecords+1));

これは1つの可能な方法のように思えるかもしれませんが、残念ながら、構造体についての保証はありません。したがって、実際にはメンバー間にパディングを含めることができるため、構造体の合計サイズは、アドレスがおそらくあることは言うまでもなく、結合されたメンバーよりも実際に大きくなる可能性があります。異なる。

編集

どうやら、構造体のタイプを推測するだけで構造体のサイズを取得できるようです

それで:

pStudentRecord g_ppRecords = malloc(sizeof(*g_ppRecords) * g_numRecords);

正常に動作します!

于 2012-09-15T04:14:08.040 に答える