
プログラミングC:構造
この記事はシリーズの一部です – どのようにプログラミングするには:Cプログラミング
序文
これまでは、互いに分離したデータを扱ってきました。 ある変数にintを、別の変数にcharを、そしてさまざまな種類の変数を配列として宣言できます。 しかし、データがお互いに関係しているとどうなりますか? たとえば、従業員のリストを追跡するプログラムがあるとします。 各従業員の時間単価、およびこの給料期間に何時間働いたかを知る必要があります。 私たちは次のようなものを設定することができます:
// we want to keep track of sixty employees char employeeName[60][25]; float employeeRate[60]; int employeeHours[60];
特定の従業員にアクセスしたいときは、特定のインデックス番号を使用できます。 たとえば、従業員番号23について知りたければ、次のように書きます:
// access employee 23 employeeName[23]; employeeRate[23]; employeeHours[23]; // compute how much to pay: float pay = employeeRate[23] * employeeHours[23];
それはうまくいくように見えますが、このアプローチには大きな欠点があります。 まず、まだ説明していませんが、これらの変数のそれぞれを、このデータで動作する可能性のある関数に3つすべて渡したくない場合は、グローバル変数でなければなりません。 第2に、各配列は互いに分離されています。コードに誤字などがあるとどうなりますか?また、従業員23の従業員情報の一部が誤って従業員25から読み込まれたり書き込まれたりしますか? さらに、プログラムで可能な従業員数を変更したい場合は、従業員数を増やすために3つのコードを編集する必要があります。 最後に、ポインタ・アドレッシングを伴うforループを使用するとしましょう。 特定の従業員のすべてのデータにアクセスできるようにするには、3つの別々のポインタをそれぞれの配列に更新する必要があります。 これらの問題はすべて問題があり、厄介であり、幸運にも良い方法があります。
構造
特定の従業員のすべての関連情報を1つのデータタイプにまとめる方法があります。 どうやって? 構造の使用。 構造体は集約データ型です。つまり、異なるデータを集約または集めて1つの見出しの下に置くことを意味します。 私たちの場合、従業員の名前、レート、時間などのすべての関連要素を集め、それらを1つのデータ型として1か所に配置することができます。 それは次のようになります。
struct employee { char name[25]; float rate; int hours; };
現在、プログラムでは実際にemployeeという新しいデータ型が作成され、実際にこのデータ型を使用して新しい変数を作成できるようになります 。 このコードスニペットは、構造宣言を示しています。 新しいデータを実際に作成しなかったので、特定のデータのフィールドを新しい変数やデータの作成に使用できるテンプレートに指定するだけでした。 この構造体宣言はテンプレートのみです。 構造体の型の変数を宣言することによって、特定の従業員レコードを作成またはインスタンス化します。 たとえば、robertという名前の従業員型変数を作成します。
struct employee robert;
これで、名前配列、レート、および時間数を含む変数robertが得られました。 配列と同様に、robertが作成されると、コンパイラは構造体が存在する必要があるすべての領域を持つように自動的にメモリを設定します。 これは、連続したスペースに、25要素のchar配列、float変数、およびinteger変数を作成します。 あなたは次のようにそれを想像することができます:
別の従業員変数を作成する場合は、johnと言います。 ジョンの名前フィールドは、ロバートの同じ名前フィールドではありません。 ジョンとロバートはそれぞれ独自の記憶と独自のフィールド値を持っています。
実際には、struct宣言自体にstruct権利を含む変数を宣言できます。
struct employee { char name[25]; float rate; int hours; } robert, john, andrea;
この場合、robert、john、andreaはすべて、employeeのテンプレートにstructを含む別々の変数になります。 だから、一般化されたstruct宣言は次のようになります:
struct struct_name { data_type field_name; data_type field_name; .... } variable1, variable2, ... variablen;
構造体の素晴らしい点の1つは、構造体が互いに同じであることが認められ、すべてのデータが構造体間で転送されるということです。 たとえば、従業員の構造例では、次のように書くことができます。
robert = john;
また、john構造体変数に保持されているすべてのデータがrobert構造体変数にコピーされます。 このようにして、構造体の各フィールドをロバートにコピーする必要はありません。 しかし、私は構造体のフィールドにどのようにアクセスするだろうと言う場合、私は名前変数が欲しいだけですか?
構造フィールドへのアクセス
演算子*と&を持つポインタのように、構造体には演算子があります:.(ドット)と ->(矢印)。 最初にドット演算子を調べます。
struct型の変数を持つ場合、変数名にドット(.)を追加してから、structに指定されたフィールドの名前を指定することで、構造体のフィールドにアクセスできます。 従業員の例では、私はrobert変数の時間単位のレートを参照したいとします。 私はこのようなものを書くだろう:
float r = robert.rate;
これはrobertのrate変数にアクセスします。 john、andrea、robertはすべて異なるメモリスロットを使い、構造体で定義されたフィールドにはそれぞれ独自の値があることに注意してください。 特定の構造体の変数のフィールドにアクセスするための一般化されたテンプレートは次のとおりです。
variable_name.field_name
しかし、特定のstructの変数がstructへのポインタの場合はどうでしょうか? 構造体へのポインタを宣言するには、基本データ型のポインタを作成するときと同じように、データ型の後で変数の前にアスタリスクを配置します。 私たちのプログラムには次のようなものがあります:
struct employee *empPointer; struct employee employeeVariable; empPointer = &employeeVariable;
この例では、empPointerはemployeeVariableへのポインタです。 empPointerが指す構造体の要素には、ポインタの解決や元のemployeeVariableの使用をせずにアクセスできます。 これは、矢印( – >)演算子を使用して行います。 それはダッシュで、より大きい記号が続きます。 矢印演算子は、構造体へのポインタに使用されている点を除いて、上記のドット演算子と同様に使用されます。 たとえば、empPointerが指す従業員レコードの時間別レートにアクセスしたいとします。 次のような記述をします:
empPointer->rate;
このようにして、構造体へのポインタは、関数とプログラムの間でやりとりでき、引き続きフィールドにアクセスできます。 構造体のフィールドにアクセスする前にすべてのポインタを解決しなければならない場合、構造体にアクセスできない場合があります。
フィールド配列と構造配列
構造変数の配列を定義するのは完全に有効ですが、インデックスを作成するものがわかるようにする必要があります。 従業員の例をさらに取り上げ、配列を必要とするとしましょう(前の記事で配列について議論します)。 私たちは次のようなものを書くかもしれません:
struct employee employees[100];
ここでは、employeesという名前の配列を100要素の長さの従業員構造のシーケンスとして定義します。 さて、上の従業員の構造には、名前の形の配列(25要素のchar配列)があります。 私たちの配列では、従業員番号25、または従業員番号25の時間別料金にアクセスしたいとします。
employees[25]; employees[25].rate;
ここでは、構造要素の配列を最初に索引付けすることに注意してください。 たとえば、配列の従業員番号15の名前の6番目の文字にアクセスしたかったとします。 このようにして、構造要素を最初に索引付けし、次にフィールド配列を2番目に索引付けします。
employees[15].name[5];
構造体の配列は構造体に含まれる可能性のある配列を変更するものではなく、外側から各配列にアクセスするだけで済みます。つまり構造体配列自体の外側の配列、次にドットの後ろの内側の配列 オペレーター。
構造内の構造
実際には、テンプレート内の他の構造を利用する構造を構築することは可能です。 これはしばしば入れ子構造と呼ばれます。 たとえば、住所の構造を定義し、そのアドレス構造を従業員の構造の各レコードに使用することを考えました。 私たちは次のようなことを書いてみるかも
struct address { char street[30]; char city[25]; char state[2]; char zip[11]; }; struct employee { char name[25]; float rate; int hours; struct address addr; };
ここでは、住所構造が「埋め込まれている」か、従業員の構造に含まれています。 メモリレイアウトは賢明であり、各構造はレイアウトを尊重しますが、従業員構造はアドレス構造に対応するためにインスタンス化に十分なスペースを作成します。 アドレス構造は、代入やその他の操作で通常の構造と同様に動作し、実行されます。 このような状況が発生する唯一の特別な表記法は、フィールドにアクセスするときに追加のドット表記法です。 この意味では、最も外側の構造にアクセスし、「最も内側の」埋め込み構造に移動する必要があります。 コードスニペットでは、
struct employee robert; // access an employee struct field robert.rate = 15.46; // access an address struct field of an employee zip = robert.addr.zip;
従業員structに含まれるstructアドレスのジップ配列にアクセスするには、ドット演算子を2回使用する必要があることがわかります。 structsへのポインタを扱うときには、通常はドット演算子が現れる矢印演算子を使うことができます。
C99フレキシブル配列構造体
C言語の新しいバージョンの1つであるC99では、構造体の最後のメンバーとしてサイズの指定されていない配列を指定できます。 最後の要素でなければならないことを覚えておいてください。そうでなければ、配列の大きさが分からないため、コンパイラは残りの要素をどこに置くべきか分かりません。 この縮小された配列は、フレキシブルな配列メンバーとして知られています。
あなたはそのようなフィールドを以下のように定義します:
struct Employee { float rate; int hours; char name[]; }
これは、我々がまだ扱っていないさまざまなもの、特にオペレータの大きさ、および構造体に動的にメモリを割り当てる方法を、別々に動作させる可能性があります。 それらの警告は、出てくるように対処されます。
C99指定された初期化子
C99では、配列と同様に、構造体のデータ型の変数を指定するときに、構造体のフィールドに値を指定することもできます。 これを行うには、次の構文を使用します。
.fieldname = value
これは、変数宣言の後に設定される中括弧の中に挿入されます。例:
struct Employee { char name[25]; float rate; int hours; } roger = { .rate = 10.5, .hours = 7 }; struct Employee gillian = { .rate = 34, .hours = 2 };
これは、変数が宣言されるとすぐに、構造体の値を与えられた値に初期化します。 ただし、これはC99のみの機能です。
結論
構造は、関連データをおそらく1つのデータ型でカプセル化する方法です。 これにより、お互いに関連するデータを1つの場所にまとめることができます。 さもなければ、私たちのデータはすべて別々の変数になければなりません。プログラムしたように、どうやって自分たちとつながっているのかを覚えておく必要があります。 これは本当に劇的なエラーになりがちで、メモリが賢明です。 特に関数になるときは、常に無数の変数を渡すか、グローバル変数がたくさんある必要があります。 構造によって、類似のデータをまとめることができます。 住所は住所フィールドを保持することができ、従業員構造は従業員に関する情報を保持することができます。 構造体は、次の構造体ポインタや前の構造体ポインタなどのデータに追加フィールドを追加できるようになるため、データ構造体に入るときにはさらに重要になります。
この記事はシリーズの一部です – どのようにプログラミングするには:Cプログラミング
この記事を読んでいただければ、パトリオンのサポートを検討することもできます。
しかし、毎月の約束が少しでもあれば、私はそれを得る、あなたは私にコーヒーを買うことを考えるかもしれない。
photo credit: Kᵉⁿ Lᵃⁿᵉ Ray and Maria Stata Center at MIT (Cambridge MA) via photopin (license)