「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に入っている星座を出力しています。
結果は以下の通りです。
タグ: ポインタ
ポインタを使ってみよう
この章ではポインタについての具体例と配列のアドレスとポインタについて説明します。
以下の例は変数のアドレスを関数に渡し、ポインタ変数で受け取っている例です。 関数本体では以下のようにアドレスをポインタを受け取ります。 tashizan関数の中で*aと5を足して*aに代入し、*bと5を足して*bに代入しています。
*aには変数xのアドレスが入っているので、*aとxは同じ意味になります。
*bには変数yのアドレスが入っているので、*bとyは同じ意味になります。
つまり、*aは2と5を足して7になりましたが、*aが7になることによって、main関数の中のxが7に変わっています。
*bは4と5を足して9になりましたが、*bが9になることによって、main関数の中のyが9に変わっています。 次の例では配列のアドレスとポインタについて説明します。 もしくは以下のように書くことができます。 printf関数の変換指定文字を「%p」にして配列を出力すると配列の先頭のアドレスを取得することが出来ます。
つまり、配列のアドレスは配列の先頭の要素のアドレスになります。
つまり、配列名を書くだけで配列xの一番前のアドレスが取得出来ます。
と同じ結果はと書いても得ることが出来ます。
つまり、xと&x[0] (配列の先頭の要素のアドレス)はまったく同じアドレスです。
配列名でアドレスを取得するのとは違ってのアドレスを取得するには「&」を付けなくてはいけません。 それはのようにと記述してください。
xが先頭のアドレスを示すので、+1を付け加えることで先頭から一つアドレスを進めると言う意味になります。
つまり、と同じ意味になります。
は前から2番目のアドレスです。
*(x+0)と書くと配列の先頭要素を取り出すことが出来ます。
これを書き換えるととなります。
配列の2番目の要素を取り出すにはと書きます。
+1は先頭から一つアドレスを進めると言う意味になります。 結果は以下の通りです。次の例ではポインタの加算を使った値の書き換えの方法について説明します。 hairetsu(x);の箇所で配列xのアドレスをhairetsu関数のポインタ変数に渡しています。 hairetsu関数内で書き換えた結果、main関数の配列xの要素である「1,2,3」が「34,46,98」に書き換えられます。
先ほどの例をfor文を使って書き換えると以下のようになります。 次はポインタに関する演算について説明します。 この例ではポインタ *gに配列xのアドレスを入れています。
1 gにはxの先頭要素のアドレスが入っているので、「*」を付けることで1番前の要素を得ることが出来ます。
結果は4です。 g++;とありますが、これはポインタの指している位置を1つ先に進めると言う意味です。
ポインタを進めた後にと出力すると、前から2番目の要素の値である6を得ることが出来ます。
さらにポインタの指している位置を進めると8が得られます。
3 この箇所はポインタを戻しています。
ポインタをのように減算子を使って書くと、その時点のポインタから1つ後ろに戻る動作をします。
つまり8の時点から1つ下がるので6が得られます。
では次の例を見てみましょう。 これまでは配列のアドレスを関数本体に渡す時、ポインタで受け取っていましたが今度は他の方法で配列を関数に渡します。
hairetsu(x);のように配列のアドレスを渡すまでは同じですが、関数本体のほうはのように配列で受け取っています。
そして要素の数(3)だけ繰り返しループして、の結果をg配列に代入しています。
つまりg[i]のiに0から2までが代入され、その都度の計算結果が代入されることになります。 配列xと配列gはまったく同じなので(xもgも同じアドレスを示しています)、この例の結果は「10と10と10」になります。
では次の例に進みます。 仮引数にポインタを指定した場合、これまでは のように代入していましたが、実はこのような難しい表現を使わなくてものようにポインタにを付けて値を代入することもできます。
g[i]のiに0から2までが代入され、その都度の計算結果を代入します。 結果は「6と6と6」です。
次は文字列を扱う配列とポインタの関係について説明します。 配列の章で学習しましたが、文字列を配列で扱う時には型をcharにして、のように書きます。
と同じことをポインタを使って書き換えるとのように書くことが出来ます。
出力する時には もしくは と書きます。 ポインタを使ったのような書き方では再度のように代入(上書き)することができます。
では次の例に進みます。 関数の引数にポインタを使用している場合に、引数の値を変更したくない時があります。
そのような時にconstを変数の前に指定します。
constをつけることにより、引数の中身を書き換えないことを明確にできるメリットがあります。
関数の引数に const を指定すると、その関数の中では値を書き換えることができなくなります。
ですので、のような記述はできませんので、この例はエラーになります。
次の例は2次元配列とポインタの関係を学習します。 複数の文字列を配列に入れるには配列のポインタを使用します。 例ではの箇所です。
出力する方法は普通の配列と同じように添え字を使って出力します。
この章ではポインタについて説明します。
「ポインタ」を理解するためにはコンピュータがどのようにデータを記憶しているかの理解が必要です。
変数の値をコンピュータが記憶する場合、そのデータはメモリ上に置かれます。
メモリとは, 1バイトの幅の区画がビルの階のように連続してつながっているものと思ってください。
例えば、ビルは下から順番に1,2…と階数が付けられますが、メモリも同じで「1バイトの幅の区画」に順番に「番号」が付けられています。
この番号を「アドレス」と言います。
このアドレスは例えば0026FDE8のような16進数で表します。
変数を記憶するには1バイトで済むとは限らず、型によっては何バイトも必要になることがあります。
変数の型によって、使うバイト数が決まると言うことです。 では最初に戻ってアドレスについて詳しく説明します。
例えば2147483647と言う大きな整数を使用して4バイト分を使うとします。
この整数のアドレス(メモリ上で)はどこにあると思いますか?
実はこの整数のアドレスはその整数に割り当てられたアドレスの先頭のアドレスにあります。 ではアドレスが分かったところで次の例でポインタについて説明します。 ここではsizeof演算子で変数の大きさを測っています。
結果をバイト単位で表します。
変数iはsizeof演算子で調べた結果、4バイトあることがわかります。 変数iのアドレスを知るには変数の前に「&」を付けます。
これを付けることによって、変数iの値がメモリのどこに記憶されているか知ることが出来ます。
「&」をアドレス演算子と言います。
このアドレスは使っているコンピュータによって違います。
例えば「0044FA6C」などと表示されます。
そして、そのアドレスを出力するためのprintf関数の変換指定文字は「%p」を使います。
もしくは「std::cout」を使って以下のように書いてもいいです。 ここで「ポインタ」について説明します。
ポインタとは変数のアドレスを記憶する特殊な変数のことです。
これをポインタ変数と言います。
つまり、アドレスを記憶するだけの変数を作ることが出来るのです。
ポインタ変数も普通の変数と同じく宣言しなくてはいけません。 例ではと宣言しています。
そしてポインタの宣言をした後で、のように変数iのアドレスをポインタ変数gに代入します。
以下のように宣言と初期化を一緒に書くこともできます。 これで変数iのアドレスがポインタgに代入されましたので、の結果もと同じく0044FA6Cになります。
以下のように書いても同じ意味です。 ここでさらにポインタについての補足をします。先ほどiのアドレスをポインタgに入れましたが、これにより、iとgは同じアドレスを保有することになりました。
抽象的に言いますと、i君とgさんが同居することを意味していまして、gに起こった出来事はiに起こったことと同じ意味を持つようになります。
ですので一身同体のゆえ、次の5番ではポインタgからiの値を取り出しています。 gには変数iのアドレスが入っていますが、「*g」のようにという形で出力すると変数iの値を得ることが出来ます。
つまり、iとgには同じアドレスが入っているので、「*g」と書くことでiに入っている値が取り出すことが出来ます。
この例の結果は2147483647となり、iの値と同じになります。「*g」に2147483600を代入した後にiを出力するとどうなると思いますか?
結果は「2147483600」になります。 ポインタ変数「*g」は変数iの代わりに使うことが出来ることを意味します。
次の章で具体的なポインタの使い方について説明します。
構造体について知ろう
この章では構造体について説明します。
「構造体」はバラバラの異なる型のデータを持つ変数同士を1つの型として扱うことができます。
それに対して配列は単一のデータ型しかまとめて扱う事はできません。
例えば、学生の情報を扱いたい場合に
「成績はint型」
「身長はdouble型」
「名前はchar型」
など様々なデータ型なので配列では扱うことが出来ません。
このような時に構造体を使います。
では例をみてみましょう。 構造体の作り方は最初に「構造体の宣言」を以下のように書きます。 この例では以下の部分が「構造体の宣言」です。 構造体型を宣言する時はstructを使います。構造体型の名前は自由に付けてください。
そしてstructのブロックの中にメンバ(変数や配列)の宣言をして、終わりにを付けます。
メンバとは変数や配列の別名だと思ってください。
構造体の宣言は「関数の外」で宣言するとその場所から下のプログラムのすべてで使うことが出来ますが、関数の中で宣言すると関数の中でのみ使うことが出来ます。
構造体型を宣言した後は「構造体型に属する変数」を作ります。
構造体型の変数も普通の変数と同じように変数の宣言と値の代入をしなくてはいけません。
以下の基本構文が構造体型変数の宣言と初期化です。 この例では以下の箇所が構造体型変数の宣言と初期化です。 {1,18,”koike”,170.2}は以下の構造体のメンバに上から順に代入されます。 つまり、以下のようにデータが代入されます。 構造体型の配列でも以下の構文でメンバに代入することができます。 構造体型の配列も通常の配列と同じで宣言と同時に初期化することが出来ます。
例では以下のように書いています。 ではプログラムの説明に入ります。 この時、気を付けなくてはいけないのはの中の要素の書き順は構造体の宣言の中のメンバの順番と合わせてください。
例えばのような書き方はいけません。 例えばint id;のデータを得たい時にはという形式で、と記述します。
これで1が出力されます。
構造体の配列はfor文で出力しています。 iには0から2がループするごとに代入されます。 先ほどの例では構造体型の変数と配列の2つを使いましたが、以下のように構造体型の配列だけ使用しても結果は同じになります。
次に構造体型の型名を短縮する書き方について説明します。 今までは構造体を宣言するには のように長い型名を書かなくてはいけませんでした。
次の構文で書くと構造体型の型名を省略することが出来ます。
〇構造体型の宣言をする時に構造体型名の前にtypedefを付ける。
〇構造体型の変数や配列を宣言する時にと書く代わりにどのような名前を使いたいかを決めてブロックの終わりに記述します。
この例ではになります。これはstudentではなくても何でもいいです。 上のように宣言しましたので以下のようにstructを付けないで宣言と初期化をすることが出来ます。 この書き方はintなどで宣言するのと同じ感覚で書くことができますので、使いやすいです。
次の例では構造体でのscanfの使い方について説明します。 scanfの使い方が構造体では違うということではありませんが、scanfと構造体の組み合わせで書けることを覚えておいてください。 この例では以下の箇所が該当します。 補足ですが、今までは構造体の変数にデータを入れる場合には以下のように書いていました。 この例の場合には以下のように構造体の宣言をしてから、scanfでデータを入れています。
次の例では構造体を関数に渡す方法について説明します。 実引数がのように構造体型変数なので、仮引数ものように構造体変数で受け取ります。
メンバの値を出力するにはという形式で書きます。 次のyo関数にはseito2という構造体配列を渡しているので、関数側でも引数には構造体型の配列であるを設置します。 結果は以下の通りです。
次の例では構造体型のポインタを使用する方法について説明します。 ここでは構造体型の変数のアドレスを実引数にしてhu関数を呼び出しています。
ですので仮引数では以下の通り構造体型のポインタで受けています。
ここに見慣れないという記号がありますが、これはアロー演算子と呼ばれています。
アロー演算子は構造体型のポインタから構造体のメンバにと、アクセスするために使います。
ここでは構造体型の配列のアドレスを引数にしてyo関数を呼び出しています。
実引数であるseito2配列をyo関数の中の構造体型のポインタ(仮引数)で受けています。
この構造体型の配列の要素にアクセスするには以下のようにポインタを進めながら書きます。この時にはアロー演算子を使います。 つまり、iの値がループするごとに進むので、ポインタが示す位置も変わります。
次の例では構造体の配列について説明します。
以下の例ではscanfを使って、入力した値を構造体の配列に入れています。 構造体の配列は以下のようにブロックの末尾に配列名と要素数を配置します。 この例では以下のscanfで入力した値をメンバに代入します。
scanfの引数にあるメンバはカンマで区切れば複数書くことができます。
変換指定子もメンバの数だけ書いてください。 そして、入力した値をprintfで出力します。 結果は以下の通りです。
ポインタを使ってみよう
この章ではポインタについての具体例と配列のアドレスについて説明します。
以下の例は変数のアドレスを関数に渡し、ポインタ変数で受け取っている例です。 tashizan関数の中では*aと5を足して*aに代入し、*bと5を足して*bに代入しています。
*aには変数xのアドレスが入っているので、*aとxは同じ意味になります。
*bには変数yのアドレスが入っているので、*bとyは同じ意味になります。
次の例では配列のアドレスとポインタについて説明します。初めに配列のアドレスを取得する方法について説明します。 つまり、配列名を書くだけで配列xの一番前のアドレスが取得できます。
配列自体のアドレスは配列の先頭アドレスを指すということです。
xが先頭のアドレスを示すので、「+1」を付け加えることで先頭から一つアドレスを進めると言う意味になります。
これはと同じ意味になります。
は前から2番目のアドレスを意味します。
では前から3番目のアドレスを得るためにはどうしたらいいでしょうか?
結果は以下のようにもしくはと書いてください。 次に配列の要素をポインタで取り出す方法について説明します。 =>*(x+0)と書くと配列の先頭要素を取り出すことが出来ます。 「数値」に添え字を書きますとその添え字に相当する要素を取得できます。
「*(x+0)」は「*x」と同じですので、以下のようにも書くことができます。 つまり、「*配列名」と書いても配列の0番目の要素を取得できます。
配列の前から2番目の要素を取得するにはと書きます。
配列の前から3番目の要素を取得するにはと書きます。
結果は以下の通りです。
次の例ではポインタの加算を使った値の書き換え方法について説明します。 hairetsu(x);の箇所で配列xのアドレスをhairetsu関数のポインタ変数であるに渡しています。
そして、*(g+0)=34;の箇所で配列の一番前の要素に34を代入して、
*(g+1)=46;の箇所で配列の前から二番目の要素に46を代入して、
*(g+2)=98;の箇所で配列の前から三番目の要素に98を代入しています。 hairetsu関数内で書き換えた結果、main関数の配列xの要素である「1,2,3」が「34,46,98」に書き換えられます。
先ほどの例をfor文を使って書き換えると以下のようになります。
次はポインタに関する演算について説明します。この例ではポインタ*gに配列xのアドレスを入れています。 gにはxの先頭要素のアドレスが入っているので、「*」を付けることで1番前の要素の値を得ることができます。
結果は4です。 g++;(加算子)とありますが、これはポインタを1つ先に進めると言う意味です。
つまり、ポインタに加算子を使っているわけです。
最初のg++;でポインタを進めた後にと出力すると、前から2番目の要素の値である6を得ることが出来ます。
さらに2つ目のg++;でポインタを進めると8が得られます。
では次の例に進みます。 これまでは配列のアドレスを関数本体の仮引数に渡す時、ポインタで受け取っていましたが、今度は他の方法で配列を関数に渡します。
hairetsu(x);のように配列のアドレスを渡すまでは同じですが、関数本体のほうはのように配列で受け取っています。
そして要素の数(3)だけ繰り返しループして、の結果をg配列に代入しています。
つまりg[i]のiに0から2までが代入され、その都度の計算結果が代入されることになります。 配列xと配列gはまったく同じなので(xもgも同じアドレスを示しています)、この例の結果はになります。
では次の例に進みます。仮引数にポインタを指定した場合、これまでは のように代入していましたが、実はこのような難しい表現を使わなくても、のようにポインタにを付けて値を代入することもできます。
g[i]のiに0から2までが代入され、その都度の計算結果を代入します。 結果は「6と6と6」です。
次は文字列を扱う配列とポインタの関係について説明します。 配列の章で学習しましたが、文字列を配列で扱う時には型をcharにして、のように書きます。
この配列をポインタを使って書き換えると以下のように書くことが出来ます。 このようにchar型のポインタに文字列を入れることができます。
では次の例に進みます。 関数の引数にポインタを使用している場合に、引数の値を変更したくない時があります。
そのような時にconstを仮引数の前に指定します。
関数の引数に const を指定すると、その関数の中では値を書き換えることができなくなります。
ですので、のような記述はできませんので、この例はエラーになります。
次の例は2次元配列とポインタの関係について説明します。 複数の文字列を配列に入れるには配列のポインタを使用します。
基本構文は以下の通りです。 配列のポインタを出力する方法は普通の配列と同じように添え字を使って出力します。
この章ではポインタについて説明します。
「ポインタ」を理解するためにはコンピュータがどのようにデータを記憶しているのかの理解が必要です。
変数の値をコンピュータが記憶する場合、そのデータはメモリ上に置かれます。
メモリとは, 1バイトの幅の区画がビルの階のように連続してつながっているものと思ってください。
例えば、ビルは下から順番に「1,2…」と階数が付けられますが、メモリも同じで順番に「1バイトの幅の区画」に番号が付けられています。
この番号をアドレスと言います。
このアドレスは例えば0026FDE8のように16進数で表します。
変数を記憶するには1バイトで済むとは限らず、型によっては何バイトも必要になることがあります。
変数の型によって、使う大きさ(バイト数)が決まると言うことです。 では最初に戻って「アドレス」について詳しく説明します。
例えば2147483647と言う大きな整数を使用して4バイト分を使うとします。
この整数のアドレス(メモリ上において)はどこにあると思いますか?
実はこの整数のアドレスは「その整数に割り当てられたアドレスの先頭のアドレス」にあります。
では次の例でポインタについて説明します。
結果は以下の通りです。 ここではsizeof演算子で変数の大きさを測っています。
結果はバイト単位で表します。
変数iはsizeof演算子で調べた結果、4バイトあることがわかります。 変数iのアドレスを知るには変数の前に&を付けます。これを付けることによって、変数iの値がメモリのどこに記憶されているか知ることができます。
&をアドレス演算子と言います。
このアドレスは使っているコンピュータによって違います。
例えばなどと出力されます。
そして、そのアドレスを出力するためのprintf関数の変換指定文字はを使います。 ここで「ポインタ」について説明します。
ポインタとは変数のアドレスを記憶する特殊な変数のことです(ポインタ変数と言います)。
つまり、アドレス(住所)を記憶するだけの変数を作ることができるのです。
ポインタ変数も普通の変数と同じく宣言しなくてはいけません。 そしてポインタの宣言をした後で、のように変数iのアドレスをポインタ変数gに代入します(ポインタとは変数のアドレスを記憶する変数のことでしたね)。 ここでさらにポインタについての補足をします。
先ほどiのアドレスをポインタgに入れましたが、これにより、iとgは同じアドレスを保有することになりました。
抽象的に言いますと、i君とgさんが同居することを意味していまして、gに起こった出来事はiに起こったことと同じ意味を持つようになります。
ですので一身同体のゆえ、次の5番ではポインタgからiの値を取り出しています。 *gに2147483600を代入して、printfでiを出力するとどうなると思いますか? 次の章で具体的なポインタの使い方について説明します。