main関数内で定義された構造体変数のメンバ変数のアドレスを自作関数の引数に構造体型のポインタ変数をもちいてポインタ渡しをしてみます

ソーラー「はい、はいはい✋


先程のプログラムで

⇩  ⇩  ⇩    」


#include <stdio.h>


typedef struct Cube{

int no;

float tate;

float yoko;

float takasa;

}Cube;


void Cubedata2baiHyouji(Cube a){


a.no= a.no*2;

a.tate= a.tate*2;

a.yoko= a.yoko*2;

a.takasa= a.takasa*2;

/*ここで各メンバ変数に格納されている値を2倍しています。*/


printf("%d\n" ,a.no);

printf("%f\n" ,a.tate);

printf("%f\n" ,a.yoko);

printf("%f\n" ,a.takasa);

/*2倍になった

メンバ変数の値を表示します。*/

}


int main(void)

{


Cube cube1={1,7.0,7.0,7.0};


Cubedata2baiHyouji(cube1);

/*実引数である構造体変数cube1を

自作関数の引数である構造体変数aに代入しました。:*/


printf("%d\n" ,cube1.no);

printf("%f\n" ,cube1.tate);

printf("%f\n" ,cube1.yoko);

printf("%f\n" ,cube1.takasa);


return 0;

}

コンパイル結果

2        

14.000000    

14.000000    

14.000000    

1  🍎

7.000000 🍎

7.000000 🍎

7.000000 🍎


ソーラー「


main関数内で定義された構造体変数cube1が

自作関数内で定義された構造体変数aに値渡しされる様子が観察できました。


🍎の部分をみてみれば


main関数内で定義された構造体変数cube1のメンバ変数に格納された数値に変化がないのが


わかりますね。


観察日記にかかなきゃ アレサ」


アレサ 「はい ソーラーさん


かきかき


6月30日


main関数内で定義された構造体変数cube1🍃のメンバ変数に格納されていた数値だけが


自作関数内で定義された構造体変数a🌳のメンバ変数に値渡しされました


値渡しですので


main関数内で定義された構造体変数cube1🍃のメンバ変数


cube1.no

cube1.tate

cube1.yoko

cube1.takasa


に格納されている数値

1

7.000000

7.000000

7.000000

自作関数内で定義された構造体変数a🌳のメンバ変数

a.no

a.tate

a.yoko

a.takasa

に渡されるだけで


main関数内で定義された構造体変数cube1🍃のメンバ変数


cube1.no

cube1.tate

cube1.yoko

cube1.takasa

は自作関数Cubedata2baiHyoujiによる操作を受けないので


main関数内で定義された構造体変数cube1🍃のメンバ変数


cube1.no

cube1.tate

cube1.yoko

cube1.takasa


に格納されている数値は

1

7.000000

7.000000

7.000000

のまま変化はありません


                       かしこ 」


ソーラー「このmain関数内で定義された構造体変数cube1🍃が、


だんだんと


🌳🌳🌳🌳🌳になっていくんだね。


楽しみだと思わないかい?」


アレサ「🍃から🌳🌳🌳🌳🌳へ(*´▽`*)


🌳🌳🌳🌳🌳以外にも


🏡と・・・


・・・それと🌹🌹🌼も育ててみたいとおもいますの。」


ソーラー 「いくらでもすきなだけ育てられると思うな


なんたって🍃はそこら中にたくさんあるからね。


さあて、まずは 構造体畑に今日も💧をまきますか・・」



ソーラー😊💦💦💦💦💦💦💦💦💦💦💦💦

      🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃


アレサ 「それっ お水を召し上がれ


       ( ^^) _旦~~

💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧

 🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃🍃



💧                              💧


      💧           💧


    💧    🌞        💧           

                         💧

             💧

   💧        💧      💧                  💧          💧    💧           💧       

                🌞 

💧      💧            💧       

💧                    💧 


       💧             💧   💧


              💧

💧        💧      💧      💧


    💧    💧     💧      💧    💧

                   🌞 

💧        💧      💧         🌞 

                     🌞  

💧      💧  🌞💧            🌈🌈🌈 💧

                     🌈🌈

  🌞   💧        🌈🌈 💧  💧    💧  💧         🌈🌈  

         🌈🌈                  💧 💧   🌈🌈  💧     💧     💧   

   🌈🌈           💧       💧  

🌈🌈

🌈


ソーラー「よ~し 水やり終了


それでは さっきの続きにいこうかな。


main関数内で定義された


構造体変数cube1のメンバ変数に格納された数値データが


自作関数内で定義された構造体変数aのメンバ変数に


値渡しされる方法があるのであれば


構造体変数cube1のメンバ変数のアドレスを


自作関数の引数となっているポインタ変数に


ポインタ渡しする方法もあるわけなんだよね。


ポインタ渡しするということは


構造体変数cube1のメンバ変数のアドレスを代入するために


ポインタ変数をつかうことになるよね?」


アレサ「はいっ、


こちらのプログラムがポインタ渡しの例となります


でも その前に


ポインタ変数について復習されますか」



ソーラー 「おおっ親切設計 じゃあ、お願いしようかな。」


アレサ「それではポインタ変数について


もう1度,御説明いたします。


ポインタ変数とは


変数のアドレスを格納するための変数です。


まず

(ここでは例として)


aの変数宣言

をおこないます。


次にポインタという変数をつくると


そのポインタという変数の中に変数aのアドレス(住所)


を格納することができます。


つまり『&a』(変数aのアドレス)をポインタという変数に


格納することができるわけです。


では実際に


そのポインタ変数を宣言してみましょう。


ポインタ変数を宣言するには


変数にしたい自分で選んだ任意の半角英数字の前に*をつけることで達成できます。


*印はそのあとにつづく文字をポインタ変数とするためにつけられるのです。


例えば


次の例のようになるわけです。


int *pta;  ( この場合ptaがポインタ変数として宣言されています。)

int *neko; (この場合nekoがポインタ変数として宣言されています。)



この例では、int型のポインタ変数宣言がおこなわれていますね。


ここで注意するのは


今までは変数aが整数の値を格納するなら


int a;

とint 型を


変数bが実数の値を格納するなら


float b;

とfloat 型を

というように


変数aや変数bに収められる数値の種類によって


変数の型が決められてきました。


ですが、


ポインタ変数の型は


ポインタ変数におさめられる数値の種類によって


intやfloat等の変数の型がきめられるわけではありません。


というか、


そもそもポインタ変数に格納されるのは変数のアドレスなので


格納されるのは常に整数です。

(%p出力変換指定子を用いてprintf出力表示した場合)

16進数の数値となっています


それではどのように


ポインタ変数の型はきめられるのでしょうか?


その仕組みはどのようなものになっているか・・というと


それは


int型の変数aのアドレス(&a)をポインタ変数に格納するのなら


int *pta; (ここではptaがポインタ変数宣言されています。)


であらわされるように


int*型(イントアスタリスク型)にポインタ変数pta

を格納するというきまりになっています。


float型の変数bのアドレス(&b)をポインタ変数に格納するのなら


float *ptb; (ここではptbがポインタ変数宣言されています。)


であらわされるように


float*型(フロートアスタリスク型)にポインタ変数ptb

を格納するというきまりになっています。


ポインタ変数がとりうる数値の種類(実数か整数)によって


ポインタ変数が格納される型が


決まっているわけではありませんね。



ポインタ変数の復習は、ここまででよいでしたでしょうか?」


ソーラー「そうだった。そうだった」


アレサ


「そして、今回は


構造体変数cube1のメンバ変数のアドレスを


自作関数の引数に わたす


          ポインタ渡し



行いたいというわけです。


そのためには 


まずは


構造体Cube型の構造体変数cube1の


アドレス(&cube1)を


を格納することのできる


ポインタ変数を作成したいというわけです。」


ソーラー「

自作関数の引数に

変数をもちいて

main関数内で定義された変数を

自作関数の引数に代入し

main関数内で定義された変数に格納されている数値データだけを

自作関数の引数に渡す方法は


            値渡し


自作関数の引数に

ポインタ変数をもちいて

main関数内で定義された変数のアドレスを

自作関数の引数に渡す方法は


            ポインタ渡し


これは基本パターンだね。」


アレサ


「構造体Cube型の構造体変数cube1のアドレス(&cube1)を


格納できるポインタ変数を作製するには


次のポインタ変数宣言


Cube *pta;

(ここではptaがポインタ変数宣言されています。

ポインタ変数の名前はptaでもpttomatoでも何でも構いません)


のように


ポインタ変数ptaを


構造体変数cube1が格納されているCube型に対応して


Cube*型で宣言するというきまりがあります。


そうして 作製されたポインタ変数ptaに


構造体変数cube1のアドレス


&cube1を 代入します


pta=&cube1;


このとき


アドレス&cube1の場所のメモリには


構造体変数cube1のメンバ変数

cube1.no

cube1.tate

cube1.yoko

cube1.takasa

に格納されている数値が


保存されています


その


構造体変数cube1のメンバ変数

cube1.no

cube1.tate

cube1.yoko

cube1.takasa

格納されている数値は



 🍋構造体変数cube1のアドレスが格納されているptaに🍋


       🍋アロー演算子->を用いて🍋



pta->no

pta->tate

pta->yoko

pta->takasa

あらわされるのでした。」


ソーラー「🌞そうだったね😊


           🌞pta=&cube1;🌞




          🌞ポインタ変数ptaに🌞



🌞構造体変数cube1のアドレス&cube1が代入されたとき🌞


構造体変数cube1のメンバ変数


cube1.no

cube1.tate

cube1.yoko

cube1.takasa



格納されている数値は


pta->no

pta->tate

pta->yoko

pta->takasa



あらわされるんだったね。」


アレサ「


pta->no

pta->tate

pta->yoko

pta->takasa


を用いたプログラムは次のようになります



#include <stdio.h>


typedef struct Cube{

int no;

float tate;

float yoko;

float takasa;

}Cube;

int main(void)

{

Cube *pta;

Cube cube1={1,7.0,7.0,7.0};

pta=&cube1;

printf("%d\n" ,pta->no);

printf("%f\n" ,pta->tate);

printf("%f\n" ,pta->yoko);

printf("%f\n" ,pta->takasa);


return 0;

}

コンパイル結果

1

7.000000

7.000000

7.000000



ソーラー「アロー演算子 ->  ほんと矢の形なんだ。」


アレサ「ここまで 先のエピソードの復唱でした😊


それでは


ポインタ変数ptaを自作関数の引数につかって


main関数内で定義された構造体変数cube1のアドレスを


自作関数の引数にポインタ渡ししてみましょう。


そのことをよく示す


プログラムは次のようになります」



#include <stdio.h>


typedef struct Cube{

int no;

float tate;

float yoko;

float takasa;

}Cube;


void Cubedata2bai(Cube *pta){


pta->no=2*pta->no;

pta->tate=2*pta->tate;

pta->yoko=2*pta->yoko;

pta->takasa=2*pta->takasa;


/* 

void Cubedata2bai(Cube *pta)

👆

このような


自作関数(ポインタ変数宣言);


の表記方法はmain関数内で定義された変数のアドレスを


自作関数の引数にポインタ渡しするのに必要な表記法でしたね。


main関数内で定義された構造体変数cube1のアドレスを格納するために


ptaのCube*型のポインタ変数宣言を自作関数の()内で


おこないます 


構造体変数cube1のアドレスをポインタ変数ptaに渡すことにより


構造体変数cube1のメンバ変数のアドレス情報が


ポインタ変数ptaに渡されることになります


このとき


構造体変数cube1のメンバ変数のアドレスに格納されている数値は


ポインタ変数ptaを使って

pta->no

pta->tate

pta->yoko

pta->takasa


と表されます


構造体変数cube1のメンバ変数の


         アドレスに


格納されている数値は


ポインタ変数を使って

pta->no

pta->tate

pta->yoko

pta->takasa


とあらわされるので


pta->no

pta->tate

pta->yoko

pta->takasa


に操作を加えることにより


構造体変数cube1のメンバ変数の


       アドレスに


格納されている数値を


変更することができるようになります


ここでは


pta->no=2*pta->no;

pta->tate=2*pta->tate;

pta->yoko=2*pta->yoko;

pta->takasa=2*pta->takasa;


構造体変数cube1のメンバ変数のアドレスに格納されている数値を


2倍になるよう自作関数を定義しています


*/


printf("%d\n" ,pta->no);

printf("%f\n" ,pta->tate);

printf("%f\n" ,pta->yoko);

printf("%f\n" ,pta->takasa);

}


int main(void)

{


Cube cube1={1,7.0,7.0,7.0};

/*cube1の構造体変数宣言を行います*/


Cubedata2bai(&cube1);

/*main関数で定義された構造体変数cube1のアドレス&cube1を


自作関数Cubedata2baiの引数である


Cube *pta(ポインタ変数pta)に代入してポインタ渡しを行います*/


printf("%d\n" ,cube1.no);

printf("%f\n" ,cube1.tate);

printf("%f\n" ,cube1.yoko);

printf("%f\n" ,cube1.takasa);


/*この4つのprintf命令文を実行して

構造体変数

cube1のメンバ変数の格納しているアドレスが

自作関数にポインタ渡しされて

cube1のメンバ変数

の格納している数値データが

自作関数の操作を受け

2倍になっているかどうかを確認しています。*/


return 0;

}


コンパイル結果

2

14.000000

14.000000

14.000000

2        🍈

14.000000    🍈

14.000000    🍈

14.000000    🍈



アレサ「


コンパイル結果の4つの🍈を見てもわかるように


main関数内で定義された構造体変数cube1のアドレス情報を


自作関数の引数に


          「ポインタ渡し」した結果


構造体変数cube1のメンバ変数に格納されていた数値は


自作関数の操作をうけて2倍にされています。」


ソーラー「ようは


 

          「ポインタ渡し」とは


 


自作関数内でCube *ptaに定義されたポインタ変数ptaに


main関数で定義された構造体変数cube1の


           アドレスを


渡したように


自作関数の引数となっているポインタ変数に


アドレスを渡すことなんだね。


その際


構造体変数cube1のメンバ変数のアドレスに格納されている数値は


ポインタ変数をつかって


pta->no

pta->tate

pta->yoko

pta->takasa


と表され


pta->no

pta->tate

pta->yoko

pta->takasa


に格納されている数値を


pta->no

pta->tate

pta->yoko

pta->takasa

に操作を加えることにより変更できるってわけなんだね。🌞



これで


main関数内で定義された構造体変数に格納されている数値データも


自作関数をつかって変更を加えることができるようになったんだね。



このように構造体変数を自作関数にポインタ渡しする方法を使えば


シューティングゲームの弾の位置を


瞬間的に移動させる


ようなことがことができるんだね」


アレサ 「できるとおもわれますの😊


次のプログラムをご覧ください


#include <stdio.h>


typedef struct Tamanoitidata{

float tate;

float yoko;

}Tamanoitidata;


/* 弾の縦、横の2つの位置データを格納するため

構造体Tamanoitidata

を作製します */



void TamanoitidataHennkou(Tamanoitidata *pta){


/* ptaのポインタ変数宣言をおこなっています


この弾の位置を変更する自作関数TamanoitidataHennkouの


引数であるポインタ変数ptaに


後にmain関数内で定義される


弾の

縦、横の位置データを格納している


構造体変数tamanoitidata1のアドレスをポインタ渡しします。


つまり

構造体変数tamanoitidata1のメンバ変数のアドレスが


ポインタ渡しされます。


ポインタ渡しするのは


main関数で定義された


構造体変数tamanoitidata1のメンバ変数に


格納されている数値を


変更することができるようにするためですね💖 */




pta->tate=pta->tate+5.0;

pta->yoko=pta->yoko+5.0;




/*👆ここでは


自作関数の引数であるポインタ変数ptaに


構造体変数tamanoitidata1のアドレスを渡し


ポインタ渡しにすることが決まったので


あとは構造体変数tamanoitidata1に格納されている数値を


どのように変化させるかを定義していきます


その際 ポインタ変数ptaを使って


構造体変数tamanoitidata1に格納されている数値を


変化させることになります。


    🌞自作関数の引数であるポインタ変数ptaに🌞


    🌞構造体変数tamanoitidata1のアドレスを渡し🌞


ポインタ渡しにすることが決まったので


構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate

tamanoitidata1.yoko


のアドレスに格納されている数値データは


ポインタ変数ptaに

アロー演算子->を用いて


pta->tate

pta->yoko


とあらわすことができます


ここでは


pta->tate=pta->tate+5.0;

pta->yoko=pta->yoko+5.0;


と記述することにより


構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate

tamanoitidata1.yoko


のアドレスに格納されている数値に5.0をたす操作を


自作関数TamanoitidataHennkouの定義としています。


*/


printf("%f\n" ,pta->tate);

printf("%f\n" ,pta->yoko);

}


int main(void)

{


Tamanoitidata tamanoitidata1={1.0,1.0};


TamanoitidataHennkou(&tamanoitidata1);

/*構造体変数tamanoitidata1のアドレス&tamanoitidata1を


自作関数TamanoitidataHennkouの引数である


ポインタ変数宣言Tamanoitidata *ptaに代入しています


つまり ポインタ変数ptaに代入しています


ようは

自作関数の引数に

構造体変数tamanoitidata1のアドレスを渡しておいて

あとは自作関数をつかって

構造体変数tamanoitidata1のメンバ変数に格納されている値を

変化させるという仕組みになっています


*/


printf("%f\n" ,tamanoitidata1.tate);

printf("%f\n" ,tamanoitidata1.yoko);


/*この最後の2つのprintf命令文で


main関数内で定義された

構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate

tamanoitidata1.yoko


に格納されている数値が


自作関数TamanoitidataHennkouの操作を受け


tamanoitidata1.tate=1.0

tamanoitidata1.yoko=1.0から


tamanoitidata1.tate=6.0

tamanoitidata1.yoko=6.0


になっているかを確認しています*/


return 0;

}


コンパイル結果

6.000000

6.000000

6.000000

6.000000


アレサ「でてきました。(´▽`*)


コンパイル結果をご覧になられてもお分かりいただけますように


main関数内で定義された

構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate 

tamanoitidata1.yoko

のアドレスが


自作関数の引数にポインタ渡しされ


自作関数TamanoitidataHennkouの操作を受けて


main関数内で定義された

構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate 

tamanoitidata1.yoko



tamanoitidata1.tate=1.0 

tamanoitidata1.yoko =1.0


と格納されている数値データが


tamanoitidata1.tate=6.0

tamanoitidata1.yoko =6.0


と変化しました


このことは

ディスプレイ上にある


縦1.0

横1.0

にあるシューティングゲームの弾の位置が


自作関数の操作を受けて


縦6.0

横6.0

に移動することと


原理的にはおなじだとおもわれますの


これが値渡しなら


構造体変数tamanoitidata1のメンバ変数


tamanoitidata1.tate 

tamanoitidata1.yoko

自作関数の操作をうけず


tamanoitidata1.tate 

tamanoitidata1.yoko


に格納されている数値は


tamanoitidata1.tate=1.0 

tamanoitidata1.yoko =1.0


のままとなり


ディスプレイ上の


弾の位置は


縦1.0

横1.0


のまま変化しないというわけです」


ソーラー「これを応用すれば


弾の位置を少しづつ移動させて


直線や曲線を描いて見せるというようなことも


できるというわけなんだね」


アレサ「そうですね😊」