割り当てられた配列
割り当てられた配列を使用すると、従うのは簡単です。
ポインターの配列を宣言します。この配列の各要素は次を指しますstruct Test
:
struct Test *array[50];
次に、必要に応じてポインタを構造体に割り当てて割り当てます。ループを使用するのは簡単です。
array[n] = malloc(sizeof(struct Test));
次に、この配列へのポインターを宣言します。
// an explicit pointer to an array
struct Test *(*p)[] = &array; // of pointers to structs
(*p)[n]->data
これにより、 ;を使用できます。n 番目のメンバーを参照します。
この内容が混乱していても心配しないでください。それはおそらくCの最も難しい側面です。
ダイナミック リニア アレイ
構造体のブロック (事実上、構造体へのポインターではなく構造体の配列) を割り当て、そのブロックへのポインターが必要な場合は、より簡単に行うことができます。
struct Test *p = malloc(100 * sizeof(struct Test)); // allocates 100 linear
// structs
次に、このポインターを指すことができます。
struct Test **pp = &p
構造体へのポインターの配列はもうありませんが、全体が大幅に簡素化されます。
動的に割り当てられた構造体の動的配列
最も柔軟ですが、頻繁には必要ありません。最初の例と非常に似ていますが、追加の割り当てが必要です。これを実証するために、正常にコンパイルされる完全なプログラムを作成しました。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct Test {
int data;
};
int main(int argc, char **argv)
{
srand(time(NULL));
// allocate 100 pointers, effectively an array
struct Test **t_array = malloc(100 * sizeof(struct Test *));
// allocate 100 structs and have the array point to them
for (int i = 0; i < 100; i++) {
t_array[i] = malloc(sizeof(struct Test));
}
// lets fill each Test.data with a random number!
for (int i = 0; i < 100; i++) {
t_array[i]->data = rand() % 100;
}
// now define a pointer to the array
struct Test ***p = &t_array;
printf("p points to an array of pointers.\n"
"The third element of the array points to a structure,\n"
"and the data member of that structure is: %d\n", (*p)[2]->data);
return 0;
}
出力:
> p points to an array of pointers.
> The third element of the array points to a structure,
> and the data member of that structure is: 49
またはセット全体:
for (int i = 0; i < 100; i++) {
if (i % 10 == 0)
printf("\n");
printf("%3d ", (*p)[i]->data);
}
35 66 40 24 32 27 39 64 65 26
32 30 72 84 85 95 14 25 11 40
30 16 47 21 80 57 25 34 47 19
56 82 38 96 6 22 76 97 87 93
75 19 24 47 55 9 43 69 86 6
61 17 23 8 38 55 65 16 90 12
87 46 46 25 42 4 48 70 53 35
64 29 6 40 76 13 1 71 82 88
78 44 57 53 4 47 8 70 63 98
34 51 44 33 28 39 37 76 9 91
単一動的に割り当てられた構造体の動的ポインター配列
この最後の例はかなり具体的です。前の例で見たように、これはポインターの動的配列ですが、それらとは異なり、要素はすべて単一の割り当てで割り当てられます。これには用途があり、元の割り当てをそのままにして、さまざまな構成でデータを並べ替える場合に最も注目に値します。
最も基本的な単一ブロックの割り当てで行うように、要素の単一ブロックを割り当てることから始めます。
struct Test *arr = malloc(N*sizeof(*arr));
次に、ポインターの別のブロックを割り当てます。
struct Test **ptrs = malloc(N*sizeof(*ptrs));
次に、ポインター リストの各スロットに、元の配列の 1 つのアドレスを入力します。ポインタ演算により要素から要素アドレスに移動できるため、これは簡単です。
for (int i=0;i<N;++i)
ptrs[i] = arr+i;
この時点で、次の両方が同じ要素フィールドを参照しています。
arr[1].data = 1;
ptrs[1]->data = 1;
そして、上記を確認した後、その理由が明確になることを願っています.
ポインター配列と元のブロック配列の処理が完了すると、次のように解放されます。
free(ptrs);
free(arr);
ptrs[]
注:配列内の各アイテムを個別に解放するわけではありません。それは彼らが割り当てられた方法ではありません。それらは単一のブロック ( で示されるarr
) として割り当てられたので、解放する必要があります。
では、なぜ誰かがこれをやりたいのでしょうか? いくつかの理由。
まず、メモリ割り当て呼び出しの数が大幅に減少します。代わりに(ポインター配列用に 1 つ、個々の構造体用に N) 、配列ブロック用に 1 つ、ポインター配列用に 1 つの2 つN+1
しかありません。メモリ割り当ては、プログラムが要求できる最も高価な操作の 1 つであり、可能な場合は最小限に抑えることが望ましいです (注: ファイル IO は別の情報です)。
別の理由: データの同じ基本配列の複数の表現。データを昇順と降順の両方で並べ替え、両方の並べ替えられた表現を同時に利用できるようにしたいとします。データ配列を複製することもできますが、それには大量のコピーが必要になり、大量のメモリが消費されます。代わりに、追加のポインター配列を割り当て、ベース配列からのアドレスを入力してから、そのポインター配列を並べ替えます。これは、ソートされるデータが大きい場合 (アイテムごとにおそらくキロバイト、またはそれ以上) に特に大きな利点があります。元のアイテムはベース配列の元の場所に残りますが、それらをソートできる非常に効率的なメカニズムが得られます。実際に動かさなくても彼ら。アイテムへのポインタの配列をソートします。アイテムはまったく移動しません。
これを理解するのは大変なことだと思いますが、ポインターの使用法は、C 言語で実行できる多くの強力な機能を理解するために不可欠です。そのため、本を読み、記憶をリフレッシュし続けてください。戻ってきます。