第5章 関数の作成 今まで使ってばっかりだった関数 この章ではその作り方を教えていきます 第5章 関数の作成 1 値を返さない関数(1) 値を返さない関数とは、式や引数に含めることができない関数 たとえば、 srand(time(NULL)) + 1 とは書きませんよね ここでは srand関数 time関数 →値を返さない関数 →値を返す関数 です 第5章 関数の作成 2 値を返さない関数(2) #include <stdio.h> エラーが起きた時の int main(void){ メッセージを変えたい!! double height,weight,bmi; printf("身長(m) "); 数か所変えなければならない scanf("%lf",&height); マクロ使 if(height <= 0){ う? printf("エラー\n"); return 0; エラーが起きた時に } 音が出るように変えたい!! printf("体重(kg) "); scanf("%lf",&weight); 数か所変えなければならない if(weight <= 0){ マクロでは対応不可 printf("エラー\n"); return 0; } こうなった時のために bmi=weight/height/height; printf("BMI %lf\n",bmi); エラーを表示する関数を return 0; 作ろう } 第5章 関数の作成 3 値を返さない関数(3) 値を返さない、引数がない関数の定義 文法 void 関数名(void){ 関数が呼び出時に実行する文1; 文2;・・・ } 注意事項 main関数など関数の外に書く 使いたい場所より上に書く 関数を作るといっても今までの関数や文法などの 組み合わせで新たな関数を作るだけです。 未知の関数を作るわけではなく 決して難しくないので安心してください 第5章 関数の作成 4 値を返さない関数(3) #include <stdio.h> void showErrorMessage(void){ printf("エラー\n"); } int main(void){ double height,weight,bmi; printf("身長(m) "); scanf("%lf",&height); if(height <= 0){ showErrorMessage(); return 0; } printf("体重(kg) "); scanf("%lf",&weight); if(weight <= 0){ showErrorMessage(); return 0; } bmi=weight/height/height; printf("BMI %lf\n",bmi); return 0; } エラーメッセージを 表示するだけの showErrorMessage関数が できました 第5章 関数の作成 5 引数(1) ただ「エラー」と表示させるだけではなく 何がエラーなのかも表示したいですよね ここで活躍するのが、そう、引数です 改行は普通しない 値を返さない、引数のある関数の定義 文法 void 関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ } 第5章 関数の作成 6 引数(2) たとえば、名前bのint型を引数に持つ関数Aがあるとします void A(int b){ ・・・ } int main(void){ A(100); return 0; } 文法 A関数内ではbは変数のように扱える 代入、参照が可能 bの初期値はmain関数で指定した100 void 関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ } 第5章 関数の作成 7 引数(3) #include <stdio.h> printf("体重(kg) "); //causeが0なら身長,1なら体重が不正 scanf("%lf",&weight); void showErrorMessage(int cause){ if(weight <= 0){ if(cause == 0){ showErrorMessage(1); printf("身長が不正\n"); return 0; }else if(cause == 1){ } printf("体重が不正\n"); bmi=weight/height/height; } printf("BMI %lf\n",bmi); printf(“エラー\n"); return 0; } } int main(void){ double height,weight,bmi; printf("身長(m) "); scanf("%lf",&height); if(height <= 0){ showErrorMessage(0); 本来であればcauseの取りうる値を マクロにすべきですが、スペースがないので return 0; お許しください・・・ } 第5章 関数の作成 8 引数(4) 引数はコピーされた値に過ぎない → 呼び出し先関数内で引数を変更しても 呼び出し元の変数は一切影響を受けない #include <stdio.h> void a(int b){ b=1; Cには } 影響しない int main(void){ int c=0; a(c); printf("%d\n",c); return 0; } a関数 main関数 0 int c = 0; c 0 a(c); c 0 c 第5章 関数の作成 b = 1; 0 b 1 b 9 return文(1) showErrorMessage関数ですが、 引数が0,1以外の時は関数を強制終了させたいですね 値を返さない関数では return文を使って関数を強制終了できます 文法 return; では先ほどのプログラムを改造しましょう 第5章 関数の作成 10 return文(2) #include <stdio.h> void showErrorMessage(int cause){ if(cause == 0){ printf("身長が不正\n"); }else if(cause == 1){ printf("体重が不正\n"); }else{ return; } printf(“エラー\n"); } int main(void){ double height,weight,bmi; printf("身長(m) "); scanf("%lf",&height); if(height <= 0){ showErrorMessage(0); return 0; } printf("体重(kg) "); scanf("%lf",&weight); if(weight <= 0){ showErrorMessage(1); return 0; } bmi=weight/height/height; printf("BMI %lf\n",bmi); return 0; } 実行されない文を書くのは無駄だと思うかも知れませんが、 ひょっとしたら引数の指定を間違えるかもしれません 第5章 関数の作成 11 値を返す関数(1) #include <stdio.h> int main(void){ 左は n Cm を求めるプログラム int n, m, nPm, mPm, i, j; printf("n "); scanf("%d",&n); printf("m "); n Pm より、 scanf("%d",&m); n Cm nPm=1; m Pm mPm=1; r s を求める関数を作れば for(i = n;i > n - m;i--){ nPm *= i; すっきり書けるのではないか } for(j = m;j > 0;j--){ mPm *= j; } printf("nCm=%d\n",nPm/mPm); 同じような処理を複数回書くのは return 0; プログラミングでは嫌われる } P 第5章 関数の作成 12 値を返す関数(2) 改行は普通しない 値を返さない、引数のある関数の定義 文法 戻り値データ型 関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ 引数がない場合は } void また、値を返す文も必要ですね。 return文が、値を返し、関数を強制終了します 文法 return 戻り値; 値を返さない関数の時とは違い、 値を返すという役割があるので注意しよう! 第5章 関数の作成 13 値を返す関数(3) #include <stdio.h> int permutation(int n,int m){ int i , result; i = n; result = 1; for(i = n;i > n - m;i--){ result *= i; } return result; } int main(void){ int n, m, nPm, mPm; printf("n "); scanf("%d",&n); printf("m "); scanf("%d",&m); nPm = permutation(n,m); mPm = permutation(m,m); printf("nCm=%d\n",nPm / mPm); return 0; 第5章 関数の作成 } メイン関数が すっきりしましたね! またどこで順列計算を してるかも 分かりやすいです 14 関数の使い道 プログラムの改良をしやすくする 同じような文の繰り返しを避ける がありました、他に 処理のまとまりを分かりやすくする int main(void){ ・・・・ このへんで初期化処理 while(true){ ・・・・ このへんで敵を動かす ・・・・ このへんであたり判定 ・・・・ ・・・・ このへんで描画処理 ・・・・ } ・・・・ このへんで終了処理 return 0; } シューティングゲームの例 int main(void){ init(); while(true){ move(); collisionDetection(); draw(); } dispose(); 分かりやすい return 0; } 第5章 関数の作成 15 プロトタイプ宣言(1) 関数は使用する場所より上に書かなければならない 関数Aから関数Bを呼び出し、 関数Bから関数Aを呼び出す場合どうする? ここで登場するのがプロトタイプ宣言 プロトタイプ宣言とは 先に関数が存在することだけを宣言する 文法(値を返す関数) 戻り値データ型 関数名(引数1データ型,引数2データ型,……) 文法(値を返さない関数) void 関数名(引数1データ型,引数2データ型,……) これが使用場所より上にあれば、 関数の実体が下にあっても使用可能 第5章 関数の作成 16 プロトタイプ宣言(2) #include <stdio.h> permutation関数が来る int permutation(int,int) ことを予告 int main(void){ int n, m, nPm, mPm; プロトタイプ宣言 printf("n "); scanf("%d",&n); さえ上にあれば使用可 printf("m "); scanf("%d",&m); nPm = permutation(n,m); mPm = permutation(m,m); printf("nCm=%d\n",nPm / mPm); return 0; int permutation(int n,int m){ } int i , result; i = n; result = 1; for(i = n;i > n - m;i--){ result *= i; } return result; } 第5章 関数の作成 17 main関数 main関数とはプログラム起動時に呼び出される関数 だから、プログラムは int mainからスタートするのですね 戻り値はint型で以下のように決められています 0 プログラムが正常終了した 1 プログラムが異常終了した 引数も指定できますが、ここでは説明しません 第5章 関数の作成 18 ここまでのまとめ 値を返さない関数 void 関数名(データ型 引数名 ,データ型 引数…){ } 値を返さない関数はreturnで強制終了 値を返す関数 戻り値データ型 関数名(データ型 引数名,データ型 引数…){ } 値を返す関数returnで値を返せる 引数に代入しても呼び出し元の変数には反映されない プロトタイプ宣言をすれば下にある関数も呼び出せる 第5章 関数の作成 19 練習問題 問1 先ほどのmCnを求めるプログラムに 次の関数を追加せよ nの階乗を求める関数 mCnを求める関数 問2 二つの円の半径、X座標、Y座標から二つの円が 接触しているかを取得する関数を作れ。 接触している場合はtrue、そうでないときはfalseを返せ。 引数は以下の通りにせよ。 (double r1,double x1,double y1,double r2,double x2,double y2) ※r1,r2は円の半径、 x1,x2は円の中心のx座標 y1,y2は円の中心のy座標 第5章 関数の作成 20 ブロック ブロックとは{}で囲まれた文の集合のこと {}は複数の文を1文とみなすことができる 通常if文など制御文の後には1文しか置けない if( num == 0 ) return 0; 1文だけなら {}不要 if( num == 0 ){ printf("入力が不正です"); return 0; } 2文を1文とみなすために {}を付ける ifの後は1文しかこれない 第5章 関数の作成 21 変数のスコープ ブロック内で宣言された変数は、 そのブロック内のみで使用することが可能 #include <stdio.h> void func(void){ printf("%d",a); //エラー printf("%d",b); //エラー } int main(void){ int a=1; if( true ){ int b = 2; printf("%d",a); //OK printf("%d",b); //OK } printf("%d",a); //OK printf("%d",b); //エラー return 0; } 第5章 関数の作成 使用できる範囲のことを スコープと言います 22 変数の寿命(1) 次のプログラムを実行しよう #include <stdio.h> void showZero(bool isInit){ int zero; if( isInit ) zero = 0; printf("%d\n“,zero); } int main(void){ showZero(true); showZero(false); showZero(false); showZero(false); return 0; 簡単に訳すと } 変数zeroを代入せずに使うな! ということです 第5章 関数の作成 23 変数の寿命(2) しかし一回目にshowZero関数を呼び出したときに 確かにzeroに0を代入しました 実はブロック内で宣言された変数の値は ブロックが終了した地点で破壊されます もう一度その部分を通っても跡形もありません 一回目にzeroに0を代入しても、 二回目にはその値は破壊されています そこで出てくるのがグローバル変数 第5章 関数の作成 24 グローバル変数(1) グローバル変数の特徴 スコープ 寿命 文法 ファイル内全て プログラムが終了するまで データ型 変数名; 文法 データ型 変数名 = 初期値; 書く場所 使う場所より上 関数の外 ※逆にブロック内の変数をローカル変数と呼ぶ 第5章 関数の作成 25 グローバル変数(2) 先ほどのプログラムをこのように修正すればうまく動くはずです #include <stdio.h> int zero; void showZero(bool isInit){ if( isInit ) zero = 0; printf("%d\n“,zero); } int main(void){ showZero(true); showZero(false); showZero(false); showZero(false); return 0; } グローバル変数があれば 関数にアクセスしたことがあるかも 取得できます。 その機能があれば 引数はいらないですね 第5章 関数の作成 26 グローバル変数(3) #include <stdio.h> int zero; bool isCalledshowZero = false; void showZero(void){ if(!isCalledshowZero){ zero = 0; isCalledshowZero=true; } printf("%d\n",zero); } int main(void){ showZero(); showZero(); showZero(); showZero(); return 0; } isCalledshowZero変数は showZero関数が 呼び出されたことないならfalse そうでなければtrueになります グローバル変数は便利ですが、 多用するとプログラムが 見にくくなります。 最小限の使用に抑えましょう。 第5章 関数の作成 27 forと変数 forの初期化の場所に変数宣言を書くことができます。 この変数は・・・ スコープ for文ブロック内 + 評価式 + 毎回実行される文 寿命 for文終了まで です。 #include <stdio.h> int main(void){ for(int i = 0 ; i <= 10 ; i++){ printf("%d\n", i ); } スコープ ここまで寿命 return 0; } 変数のスコープを極力狭くするときれいなプログラムを書ける → このプログラムは最低限のスコープしかない → 今までの方法よりも見やすくなる可能性が高い 第5章 関数の作成 28 ここまでのまとめ ローカル変数の寿命はブロックの終了まで ローカル変数のスコープはブロック内 グローバル変数の寿命はプログラム終了まで グローバル変数のスコープはファイル全体 グローバル変数の宣言は関数の外 第5章 関数の作成 29 練習問題 問1 問2 nからmまでのランダム値を返す関数を作れ。 ただし、最初に関数を読んだときのみシードの 指定をせよ 1回目その関数を呼び出したときには1を、 2回目その関数を呼び出したときには2を、 3回目その関数を呼び出したときには3を、 n回目その関数を呼び出したときにはnを 返す関数を作成せよ 第5章 関数の作成 30
© Copyright 2024 ExpyDoc