この章ではnew演算子について説明します。
プログラムはグローバル変数やローカル変数を呼び出す時にメモリの確保を行います。
グローバル変数はプログラムの初めにメモリを確保して、プログラムが終わったらメモリを開放しています。
ローカル変数は関数の呼び出し時にメモリを確保して、関数が実行し終えたらメモリが解放されます。
その他にもメモリの確保と解放をする方法があります。
それはnew演算子を使ってメモリの確保が行い、delete演算子によって解放するという方法です。
これはメモリの確保や解放のタイミングを自身で決めることを意味します。
では例を見てみましょう。 次は配列のメモリ確保と解放の例です。
投稿者: 無漏路
静的変数と静的関数について知ろう
この章では静的変数と静的メソッドについて説明します。
詳しい説明は以下の例の後に行います。 これまでのクラスはオブジェクトを作成してからメンバ変数にデータを入れていましたが、静的変数や静的メソッドはオブジェクトを作成しなくても使用することが出来ます。
つまり、これまでのクラスのメンバ変数やメンバ関数は個々のオブジェクトに属しているものと言えます。
ですので、メンバ変数はオブジェクトを複数作成しても、オブジェクト間でメンバ変数の関係性はありません。
それに対して静的変数や静的関数はクラスに属していると言えます。
後で説明しますが静的変数にはデータの連続性があります。
静的変数は、グローバル変数と同じ性質を持ちます。
クラスの中での静的変数や静的メソッドの作成方法はクラスの中に「static」を付けて作成します。 次に静的変数や静的関数を実行する方法について説明します。 例えばこの例では以下のように静的関数を実行しています。 この例にはありませんが静的変数の値を出力したい場合には以下のように書きます。 そして、この例では2回静的メソッドを実行していますが、実行するごとにcount変数に1が足されていきます。
カウントしている箇所は以下のコンストラクタの箇所です(オブジェクトを2個作成しているので、2回コンストラクタが実行されているわけです)。
つまり、静的変数はオブジェクトを何個作成しても値が1つであることが分かります。 [補足]
静的関数の中には通常のメンバ変数を入れることは出来ませんので注意してください。
結果は以下の通りです。
抽象クラスについて知ろう
この章では抽象クラスについて説明します。
今までのクラスを使ったプログラムではクラスのデータメンバやメンバ関数の内容を初めからきっちり決めて目的のプログラムを作成してきましたが、抽象クラスは文字通り抽象的な親クラスを作って、子クラスで具体的なプログラムを行っていきます。
抽象クラスで宣言をした関数は必ず子クラスで定義を書かなくてはいけないので(定義を書かないとエラー)、メンバ関数の入れ忘れなども防ぐことが出来ます。 親クラスのgetPoint関数は関数の宣言だけをして、子クラスで関数の内容を具体的に定義しています。 つまり、親クラスのメンバ関数は、宣言だけで中身がなく、関数の具体的な処理の記述は子クラスに任せると言うことです。
ですので、親クラスで純粋仮想関数を宣言すると子クラスではと宣言すると共に具体的な中身を書かなくてはいけません。 この例では以下の部分が純粋仮想関数の具体的な中身を記述した箇所です。 その場合には以下の子クラスの宣言の中でも関数の宣言を行うことが必要です。 そして、それぞれの宣言をした関数の中身を子クラスで書いていくことになります。
クラスの継承について知ろう
次はクラスの継承について説明します。
「クラスの継承」とは親クラス(元のクラス)の他に「もう1つの関連したクラス」を作る時に親クラスのデータメンバやメンバ関数を引き継ぐことを言います。
「もう1つの関連したクラス」のことを子クラスもしくはサブクラスといいます。
親クラスと同じデータメンバやメンバ関数を子クラスで定義しなくてはいけない時、親クラスと同じデータメンバやメンバ関数を子クラスで再度記述する必要がなくなります。
つまり、親クラスと同じ機能を持つデータメンバやメンバ関数を子クラスで使う時に、改めて同じデータメンバやメンバ関数を子クラスで書くのは無駄ですので、そのような時にクラスの継承を使います。
また、子クラスには子クラス独自のデータメンバやメンバ関数も追加することもできます。
注意してほしいことは全く違うクラス同士を継承しても意味が無いと言うことです。
例えば銀行クラスと自動車クラスは全く違うクラスなので、継承関係にしても意味ありません(意味があればいいですが)。
継承関係が正しいかどうか悩む場合には「子クラスは親クラスの子供である」という文の「子クラス」「親クラス」に自身で作ったクラス名を当てはめてみて、違和感がないようでしたらそれは正しい継承関係です。
「銀行クラスは自動車クラスの子供である」と聞いてもおかしいと思いませんか?
違和感があるようでしたらそれは意味が無い継承です。
では継承の例を見てみましょう。 では説明に入ります。
クラスの継承の基本構文は以下の通り書きます。 この例では以下の箇所が継承クラスの箇所です。
ここで継承クラスのポイントを説明します。
[ポイント1]
子クラスでは親クラスのデータメンバやメンバ関数を受け継いでいるので、親クラスのメンバを使うことが出来ます。
この例では子クラスのオブジェクトから親クラスのメンバ関数に以下のようにアクセスしています(親クラスのメンバをすべて使用する必要はありません)。 子クラスではdeposit関数を定義していないにも関わらずdeposit関数を使うことができるのが継承の意味です。
つまり、子クラスではprivate指定以外の親クラスのデータメンバやメンバ関数をすべて使うことが出来ます。
[ポイント2]
子クラスの引数のないオブジェクトを作成した時には先に親クラスの「引数の無いコンストラクタ」が実行されてから小クラスのコンストラクタが実行されますので初めに実行されるには以下の親クラスのコンストラクタです。
この記述で親クラスのデータメンバにデータを入れているので、子クラスで親クラスのデータメンバを使うことが出来ます。
親クラスの引数の無いコンストラクタが実行された後に、子クラスの「引数の無いコンストラクタ」が実行されます。 [ポイント3] 親クラスの定義の中に上の記述がありますが, 親クラスのメンバがprotectedで指定されている場合は子クラスの中で、そのデータメンバやメンバ関数にアクセスできます。
以下のように親クラスでprivate指定してしまうと子クラスからはアクセスすることが出来なくなります。 [ポイント4]
次は親クラスと小クラスで同じ名前のメンバ関数を定義した場合、どのような動きになるか見てみましょう。
この例では以下の部分で親クラスと子クラスで同じ関数名が使われています。 親クラスと子クラスでは関数名も引数の数や型も同じ関数を作ることが出来ます(メソッドの中身はそれぞれのクラスの内容に合わせます)。
このことをオーバーライドと言います。
この例の場合、子クラスのオブジェクトからのようにgetPoint関数を呼び出していますが、子クラスで定義された関数のほうが優先して呼び出されることになります。
この例の結果は以下の通りです。
オブジェクト指向プログラミングについて
この章ではオブジェクト指向プログラミングについて説明します。
オブジェクト指向プログラミングはC言語にはない機能であり、C++で新たに取り入れられた機能です。
オブジェクト指向型プログラミングは構造体に似た機能を持っています。
オブジェクト指向型プログラミングを使用すると変数と関数を1つにまとめることが出来ますが、この作業は「クラス」を使って行います。
つまり、「クラス」を使うことで、多くの変数と関数を1つの機能体として働かせてデータを処理することが出来ます。
クラスとは何かしらの物を作る「設計図」に例えられます。
設計図は車や家や鉛筆などをつくる場合に必要ですが、この設計図に当たるものがクラスです。
車や家や鉛筆の中には色々な部品が含まれていますが、それらの部品には「状態」や「ふるまい」があります。
例えば、鉛筆であれば、「状態」は鉛筆の形、芯、線の濃さなどで、「ふるまい」は線を引くなどの動作です。
その「状態」や「ふるまい」をクラスと言う設計図に書いていきます。
「状態」を変数で書き、「ふるまい」を関数で書くことになります。
どのような「状態」や「ふるまい」を作りたいかは「オブジェクト」の役割です。
例えば「線を書く」「線を消す」など具体的な指示をオブジェクトと言う司令塔から出します。
では初めにクラスの基本から説明します。
クラスの中に「状態」を変数に書き、「ふるまい」を関数に書きます。
クラスの中の変数をデータメンバ、関数をメンバ関数と呼びます。
これをクラスの宣言と言います。 クラス名が2文字で構成されている場合、それぞれの単語の先頭を大文字で書いて、その他は小文字で書くのが慣例になっています(この通り書かなくてもエラーにはなりません)。
次はオブジェクトの作成方法について説明します。
オブジェクトについては先ほど以下のように説明しました。 つまりオブジェクトから命令を出してクラスに対して仕事をさせる役割があります。 今まではint型などの変数を作っていましたが、オブジェクトは「クラス型の変数」を作ることを意味します。
このオブジェクトを作る過程をインスタンスと言います。
ではクラスとオブジェクトの関係のイメージをつかむためにもう一度説明します。
例えば新築の家をつくるとします。
ある人は家にプールを作ったり、床暖房を付けます。
そのことを実現するには設計図に「プールや床暖房」や「その機能」を付け加えないといけません。
実際には設計図であるクラスの中にプール、床暖房という変数を設定し、プールや床暖房の機能を定義するために関数内で水や床の温度を上げたり下げたりする機能を加える必要があります。
この設計図はそのままでは動きませんので、誰かの指揮のもとに動かさなくてはいけません。
その指揮者の役割がオブジェクトです。
オブジェクトは例えば冬になったらプールの水の温度を40度にするなど細かな指示をすることになります。
では実際に例を見てみましょう。 改めてクラスの定義の記述方法について説明します。 クラスはデータメンバとメンバ関数からできています。
クラスの中の変数をデータメンバ、クラスの中の関数をメンバ関数と呼ばれていることは説明しました。
アクセス指定子とはメンバに対して、どこからのアクセスを許可するのかを指定するものです。
つまり、クラスは複数作ることが出来ますので、どのクラスからアクセスすることができるのかを規定するのがアクセス指定子です。 クラスの中ではアクセス指定子の右側に「:」を付けて、その下にメンバ関数やデータメンバを記述します。
クラスの中に宣言をした後は、メンバ関数の実際の中身をクラスの宣言の下(つまりクラスの外)に書いていきます。 例えばgetPoint関数は以下のように書いています。 クラスの宣言の中にメンバ関数の中身を書いていく方法もありますが、説明は後ほどします。
次にクラスの中のメンバ変数にデータを入れる方法について説明します。
この例ではクラス名をkeisanと名付けていますが、クラスはただの設計図なので、これにデータを入れないと何も動きません。
データを入れるには初めにのようにオブジェクトを作ります。この例では以下の箇所です。 オブジェクトを作った後はオブジェクト変数名にドット「.」を付け、その後にデータメンバを置きます。
そして、その中に代入演算子を使ってデータを入れていきます。
つまり、 という構文でデータを入れます。 そして、次はメンバ関数を呼び出しますが、実際にメンバ関数を呼び出しているのは以下の箇所です。 tasu関数の中では入力した数が0より大きくて、200より小さい値だった場合、number変数にb変数の値を足したものをnumber変数に代入しています。
数値がそれ以外の数だった場合は が実行されてでプログラムを終了させています。
次はobj.hiku(num2);の部分を説明します。 ここでは入力した数が0より大きくて、200より小さい値だった場合、number変数からc変数の値を引いたものをnumber変数に代入しています。
number -= c;はと同じ意味なので、tasu関数で計算されたnumber変数の値からC変数を引いた値をnumber変数に代入しています。
number変数にはtasu関数で計算された値が入っているので、hiku関数ではその値が使われます。
num1に5が入っているとするとの箇所でnumberには5と12が足された数値である17が代入されます。 num2に1が入っているとするとの箇所でnumberには17から1を引いた数値である16が代入されます。
numberには上の3番で17が入っていますので、この値が使われます。 getPoint関数はnumber変数の値を返すためだけに存在します。
次の例はアクセス指定子に「private」を指定した例をみてみましょう。 この例ではnumberにprivateを指定しています。
privateもpublicと同じくアクセス指定子です。
クラスの定義の中にデータメンバやメンバ関数を記述する時にはアクセス指定子を書くことになりますが、省略も出来ます。省略した場合にはデータメンバ、メンバ関数はprivateになります。 publicは同じクラスはもちろん他のクラスからもデータメンバやメンバ関数にアクセスすることが出来ます。
privateの付いたデータメンバにはクラスの外から直接アクセスできないので、メンバ関数からアクセスすることになります。
この例のnumber変数はprivateなので、以下のsetNumber関数からnumber変数に値を入れています。 ここで補足をしておきます。
publicやprivateはデータメンバやメンバ関数に付けますが、基本はデータメンバはprivateで、メンバ関数はpublicで指定します。
データメンバをpublicにするとどのような値でも入れることが出来るので思わぬ誤作動につながる可能性があります。
ですのでsetNumber関数のような関数を作って中身のをしてからデータメンバに値を代入する方が安全です。 次は関数の引数としてオブジェクトを使用する方法について説明します。
では例をみてみましょう。 この例で説明したい箇所は以下のs2関数です。
このs2関数はメンバ関数ではない普通の関数です。
この関数にアクセスする方法について説明します。 ここでの注目点は仮引数がのようにオブジェクト変数になっていることです。
このようにオブジェクト変数でデータを受け取ることも可能になっています。
ですのでs2関数を呼び出すときには実引数にオブジェクトを入れます。 このオブジェクトを使って、普通の関数s2の中でのように書くことで、メンバ関数のtriangleArea関数やsqureArea関数などの関数を呼び出すことができるようになります。
ではこのプログラムの説明をしていきます。 結果は以下の通りです。 次の例ではコンストラクタと言う機能を学びます。 コンストラクタとはオブジェクトを作成すると必ず呼び出される特殊な関数です。
データメンバをオブジェクト作成と同時に初期化したい時にコンストラクタを使用します。
オブジェクトを作成と同時に初期化することでデータメンバに対してデータを入れ忘れることはなくなります。
以下の箇所がコンストラクタですが、numberを初期化しています。 コンストラクタの基本構文は以下の通りです。 引数があれば書きますが、無ければ書かなくても結構です。
引数を書かない場合にはコンストラクタの中でのように直接データを入れます。 引数のあるコンストラクタは以下の箇所です。 そして、コンストラクタは必ずクラスの宣言の中のpublicの中で宣言をしてください。
次にコンストラクタを呼び出す方法について説明します。
基本構文は以下の通りです。 この例のコンストラクタの呼び出しは引数が無い場合にはのように引数なしで記述し、データを渡したい時にはのように引数を書いています。
「keisan obj;」で呼び出されるコンストラクタは以下の箇所です。 「keisan obj2(5); 」で呼び出されるコンストラクタは以下の箇所です。 このようにコンストラクタは引数の型や数が違うならば同じ名前のコンストラクタを複数定義することが出来ます。
この例では引数のあるコンストラクタと引数のないコンストラクタなのでオーバーロードが可能になっています。
結果は以下の通りです。 [補足2]
クラスの中にもインライン関数を書くことが出来ます。
通常、インライン関数を書くときにはinlineを付けますが、クラス宣言の中にインライン関数を書くときにはinlineを付けなくても、インライン関数としてみなされます。
以下の赤線で囲まれている箇所がインライン関数です。
通常の関数の書き方で書きます。
他のtasu関数などもクラスの中に書いても結構です。
「参照」について知ろう
この章ではC言語にはない新しい機能である参照について学習します。
参照の機能はポインタの機能と似ています。
初めにポインタの復習から始めます。
ではポインタの例をみてみましょう。 aとpは同じメモリ領域を共有するため、「*p」に98を代入するという動作は aにも98が代入されるという意味になります。
今度は参照を使って、上のポインタの例と同じ結果を出してみます。 参照とは「変数のアドレスを指す識別子」のことです。
つまり、ポインタと同じことが出来ると思っていいです。
参照を作成するには宣言の時に「&」を付けます。 と記述していますが参照を使う場合は必ず宣言と同時に値を入れなければいけません。
つまり、以下のように参照を宣言した後に改めて変数を代入することはできません。 「b=98;」のようにbに98を代入したのに、を出力するとaまで98に変わってしまいます。
これが参照の機能です。
結果は以下の通りです。
次は関数の引数に参照を使ってみましょう。 この例では関数の引数に参照を使っています。
incre(n);の箇所でnをincre関数に渡しています。
incre関数では参照型aで値を受け取って、その値に1を足しています。
その後、で出力するとnに1が足された数が出力されます。
参照aに1を足したことで、nにも1が足されます。
つまり、aとnは同じアドレスと言うことです。
例えば4を入力すると5が出力されます。 以下の例は先ほどの例と比べて参照を使っていない以外は全く同じです。 参照を使っていないので、でnをincre関数に渡して、1を足してもnの値に影響を及ぼすことはありません。
「std::cin >> n;」で入力した値がで出力されるだけです。 次は参照の引数に「const」を付けた時の動きを見てみましょう。 関数の引数にconstを指定すると、その関数の中では値を書き換えることができなくなります。
つまり、引数aにconstを付けるとのように変更することが出来なくなります。
constを付けることにより、引数の中身を書き換えないことを明確にできるメリットがあります。
ですので、この例はエラーです。
constは仮引数が参照ではなくても、使うことができます。
ファイルの操作について知ろう
この章ではファイルの取り扱い方法について学習します。
これまではprintf関数などを使って、文字列や計算結果を画面に出力していましたが、その処理が終われば、データは失われてしまいます。
そこで、そのようなデータはファイルに保存しておけば後で確認したい時に便利です。
ではファイルの取り扱いの流れを説明します。
(1) ファイルをオープンします
(2)ファイルの読み書きをします
(3)ファイルをクローズします
では例をみてみましょう。 次にファイルをオープンするのですが、それにはfopen関数を使います。
fopen関数を実行すると、ファイル情報を持つFILE型のポインタ(ファイルポインタといいます)が返されますので、先ほどのファイル構造体の変数に代入します。 以下のようにFILE構造体の宣言とfopen関数の代入を分けて書くこともできます。 「オープンモード」はどのような状態でファイルをオープンするのかをアルファベットで指定します。以下の表を参考にしてください。
この例では”w”を使っていますが、このモードを使うとファイルの中身を上書きして、ファイルがない時は新規作成してくれます。
wモードであればこの例の「net.txt」と言うファイルが自身のフォルダに無くても、自動的に作ってくれます。 fprintf関数はファイルポインタで指定したファイルに変換指定文字を使ってデータを書き込みます。 この例では以下の箇所がfprintf関数の箇所です。 この意味はdouble型のiを変換指定文字の通りにファイルに書き込んでいます。 次に残りのプログラムの説明します。 NULLと言う見慣れない文字がありますが、ファイルが何らかの理由でオープンできなかった時にfopen関数がこのNULLを返します。
「fp ==NULL」はfpがNULLならばを出力してプログラムを終了します。
「exit(1)」はexit関数を使用していますが、引数に1を使用すると異常終了という意味になり、ファイルが開かなかった時はプログラムを終了します。
ファイルが正常に開けば「net.txt」には変数iの値である8.1が入力されます。
次の例ではscanfで読み込んだデータをファイルに書き込みます。 では説明を始めます。 while文の中にEOFという箇所がありますが、これはキーボードでを押すと返される値です(whileの条件式を2行で書いていますが、1行で書いていただいても結構です)。
EOFの前に「!」が付いているので、が押されない間は入力する行為を繰り返すという意味になります。
気を付けることは以下のfprintf関数の変換指定子の箇所に「-6」という箇所がありますが、マイナス指定をするとファイルの中のデータを左詰めにするという意味になります。ファイルに書き込むときには左詰めで入力するのが基本です。 左詰めでファイルにデータを入れているので、「net.txt」は以下のようになります。
次は「net.txt」に書き込んだデータを取り出してみましょう。 ファイルからデータを読み込むにはfscanf関数を使用します。このfscanf関数は書式指定文字に従ってデータを取り出します。 この例でfscanfが使われている箇所は以下です。 ここでもEOFが出てきますが、の意味はファイルの終端までデータを読み込むと言う意味になります。
読み込む時にはfopen関数のオープンモードは「r」になります。
結果は以下の通りです。
次の例ではfputs関数を使ってデータをファイルに書き込む方法について説明します。fputs関数はファイルポインタで指定したファイルへ1行ずつ文字列を出力します。 結果は以下の通りです。 注意点としては以下のように改行を入れないと1行でファイルに書き込まれるので気を付けてください。もちろん、初めから1行で書き込む意図があるのならば改行は入れる必要はありません。 結果は以下の通りです。 次の例は「net.txt」に書き込んだテキストファイルから指定した文字数を読み込むプログラムです。 ファイルから指定した文字を読み込むにはfgets関数が必要です。 fgetsはファイルポインタで指定したファイルから、指定した文字の数だけを読み込んで、配列に代入します。そして、読み取るものが無くなるとNULLを返すので、NULLになったらwhile文を終了します。
読み取るときのオープンモードはを指定してください。 次の例は「net.txt」に書き込んだテキストファイルから1文字ずつ読み込むプログラムです。 fgetc関数はファイルポインタで指定したファイルから1文字読み取ります。 この例では以下の箇所がfgetc関数が使われている箇所です。 ここでは読み込んだ1文字をchar型の変数に入れています。
読み込みに成功すると、その読み込んだ1文字を返し、ファイルの終わりまで読んだ場合は、EOFを返します。ここでは最後にEOFが返された時にループを終了します。
putcharは1文字ずつ出力する機能があります。
列挙型について知ろう
この章では列挙型について説明します。
「列挙型」は結びつきがある要素をひとまとめにして、新しい型として定義することを言います。
詳しい説明は例の後に行います。
列挙型は宣言を行うことが必要ですが、構文は次のようになります。 この構文をみていただくと分かりますが、定数を要素として、配列のごとく宣言されています。
例では以下のように列挙型の宣言をしています。
と宣言する事により、要素の中の定数に 0 から順に整数値が与えられます。
つまり、以下のように書き換えることが出来ます。 このようにdefineでバラバラに定数を管理しないで、列挙型で管理した方が要素間の結びつきが分かりやすくなります。
列挙型の宣言をした後は列挙型の変数を宣言します。
基本構文は以下の通りです。 この例では以下のように列挙型の変数の宣言と同時に値を初期化しています。 これにより変数doubutsuは列挙型を扱う変数として定義されたことになります。
その後はswitch文で判定して、一致した箇所で出力されます。 デフォルト値を設定しないと0 から順に整数値が与えられます。
構造体について知ろう
この章では構造体について説明します。
「構造体」はバラバラの異なる型のデータを持つ変数同士を1つの型として扱うことが出来ます。
それに対して配列は単一のデータ型しかまとめて扱う事はできません。
例えば、学生の成績はint型、身長はdouble型、名前はchar型で扱いたい時に様々なデータ型なので配列では扱うことが出来ません。
このような時に構造体を使います。
では例をみてみましょう。 構造体の宣言する時はstructを使います。
構造体型の名前は自由に付けてください。
そしてstructのブロックの中にメンバの宣言をして、終わりに「;」を付けます。
メンバとは変数の別名だと思ってください。
このメンバに後からデータを入れていきます。
この例では以下の部分が構造体の宣言です。 構造体の宣言は関数の外で宣言するとその場所から下のプログラムのすべてで使うことが出来ますが、関数の中で宣言すると関数の中でのみ使うことが出来ます。
構造体型を宣言すると構造体型に属する変数を作ることが出来るようになります。
構造体型の変数も普通の変数と同じように宣言をしなくてはいけません。
宣言をしましたら、値を入れるのですが、通常の変数と同じで宣言と同時に初期化することが出来ます。
以下の基本構文が構造体型変数の初期化です。 この例では以下の箇所が構造体型変数の宣言と初期化です。 は以下の構造体のメンバに上から順に代入されます。 構造体型の配列でもデータを入れることが出来ます。 構造体型の配列も通常の配列と同じで宣言と同時に初期化することが出来ます。
例では以下のように書いています。 ではこのプログラムの説明に入ります。
ここでは構造体の型の変数を宣言と同時に初期化しています。
この時、気を付けなくてはいけないのはの中の要素の書き順は構造体の宣言の中のメンバの順番と合わせてください。
例えば、のような書き方はいけません。 のように1つ1つ初期化するのではなく、構造体型の配列で個人のデータを初期化しています。 例えばint id;のデータを得たい時にはという形式でと記述します。
これで1が出力されます。
構造体の配列はfor文で出力しています。 iには0から2がループごとに代入され、以下のように出力されます。 先ほどの例では構造体型の変数と配列の2つを使いましたが、以下のように構造体型の配列だけ使用しても結果は同じになります。 次の例は構造体でのscanfの使い方について説明します。 scanfの使い方が構造体では違うということではありませんが、scanfと構造体の組み合わせでも書けることを覚えておいてください。 今まではのように構造体の変数に値を入れていましたが、scanfなどを使ってデータを入れる場合にはこの例のように単独で構造体の宣言を行ってから、scanfでデータを入れていきます。 結果は以下の通りです。 次の例では構造体を関数に渡す方法について説明します。 実引数がのように構造体型変数なので、仮引数ものように構造体変数で受け取ります。
メンバの値を出力するにはという形式で書きます。
メンバの値を出力するにはという形式で書きます。
次の例では構造体型のポインタを使用する方法について説明します。 ここでは構造体型変数のアドレスを実引数にして、hu関数を呼び出していますが、仮引数では構造体型のポインタで受けています。 ここに見慣れない「->」という記号がありますが、これはアロー演算子と呼ばれています。アロー演算子は構造体型のポインタから構造体のメンバにアクセスするために使います。 ここでは構造体型の配列のアドレスを引数にして、yo関数を呼び出しています。 実引数であるseito2配列をyo関数の中の構造体型のポインタで受けています。
この構造体型の配列の要素にアクセスするには のようにポインタを進めながら書きます。
つまり、iの値がループするごとに進むので、ポインタが示す位置も変わります。
次の例では構造体の配列について説明します。 この例ではscanfを使って、入力した値を構造体の配列に入れています。
初めに構造体の配列名と要素数を決めましたら、以下のようにブロックの末尾に配列名と要素数を配置します。 そして、以下のscanfで入力した値をメンバに代入します。
scanfの引数にあるメンバはカンマで区切れば複数書く事が出来ます。
変換指定子もメンバの数だけ書いてください。 そして、入力した値をprintfで出力します。 結果は以下の通りです。
関数で星座判定を作ってみよう
「if文で星座の判定をしよう」の章ではif文で星座判定のプログラムを作成しましたが、これを関数を使って書き換えてみましょう。
この例にはポインタ、2次元配列、関数、グローバル変数が含まれていますので、理解を深めてください。
では例をみてみましょう。 例えばmonthに1が入り、dayに19が入っているとします。
そうするとの箇所はiが0の時にifの条件式がtrueになりますので、以下の入れ子のif文の条件式の判定に入ります。 「day <= day1[i]」の中のdayには19が入り、1周目のにはday1の0番目の要素である19が入っていますので、条件式はtrueになり、nameにはnames配列の0番目の要素である「やぎ座」が入ります。
例えばdayに19ではなく23が入っていると仮定するとはFALSEになるので、の条件式の判定に入ります。
ここでday2[i]にはday2の0番目の要素である31が入っていますのでTRUEになり、name にはに該当する星座が入ります。
1周目ではのiには0が入っていますのでで余りを求めると1になります。
結果、となり「みずがめ座」が入ることになります。
ではをさらに詳しく説明します。
「i + 1」の箇所の説明をしますと例えばmonthが1で、day が23の場合にはがになりFALSEになるので、else ifのブロックに入ります。
そして1月は やぎ座と水瓶座しかなく、やぎ座ではないのならば水瓶座しかないのでnames配列を1つ進めるためにで1を足しています。
「%12」を記述している理由は例えばの中のiに11が入るとの箇所が12になりますが、「%12」の箇所が無いと仮定するととなってしまいます。
しかし、実際にという要素はありません。
そこで「%12」で余りを0(12割る12で余り0)にして「やぎ座」が出るようにしています。
つまり、以下の余りの数字がnames配列の添え字になり、その該当するデータがnameに入ります。 グローバル変数ですので、プログラムの中のどこからでも使用することが出来ます。そして、最後にmain関数の中でnameに入っている星座を出力しています。
結果は以下の通りです。