300 likes | 407 Views
ここから後編. 第 5 章 関数の作成. 今まで使ってばっかりだった関数 この章ではその作り方を教えていきます. 値を返さない関数 (1). 値を返さない関数とは、式や引数に含めることができない関数 たとえば、. s rand 関数 → 値を返さない関数 t ime 関数 →値を返す関数. srand (time(NULL)) + 1. とは書きませんよね. ここでは. です. 値を返さない関数 (2). #include < stdio.h > int main( void ){ double height,weight,bmi ;
E N D
ここから後編 第5章 関数の作成 今まで使ってばっかりだった関数 この章ではその作り方を教えていきます 第5章 関数の作成
値を返さない関数(1) 値を返さない関数とは、式や引数に含めることができない関数 たとえば、 • srand関数→値を返さない関数 • time関数→値を返す関数 srand(time(NULL))+ 1 とは書きませんよね ここでは です 第5章 関数の作成
値を返さない関数(2) #include <stdio.h> int main(void){ doubleheight,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) 値を返さない、引数がない関数の定義 • main関数など関数の外に書く • 使いたい場所より上に書く 文法 void 関数名(void){ 関数が呼び出時に実行する文1; 文2;・・・ } 注意事項 関数を作るといっても今までの関数や文法などの 組み合わせで新たな関数を作るだけです。 未知の関数を作るわけではなく 決して難しくないので安心してください 第5章 関数の作成
値を返さない関数(3) #include <stdio.h> voidshowErrorMessage(void){ printf("エラー\n"); } int main(void){ doubleheight,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章 関数の作成
引数(1) ただ「エラー」と表示させるだけではなく 何がエラーなのかも表示したいですよね ここで活躍するのが、そう、引数です 改行は普通しない 値を返さない、引数のある関数の定義 文法 void 関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ } 第5章 関数の作成
引数(2) たとえば、名前bのint型を引数に持つ関数Aがあるとします • A関数内ではbは変数のように扱える • 代入、参照が可能 • bの初期値はmain関数で指定した100 void A(int b){ ・・・ } int main(void){ A(100); return 0; } 文法 void 関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ } 第5章 関数の作成
引数(3) #include <stdio.h> //causeが0なら身長,1なら体重が不正 voidshowErrorMessage(int cause){ if(cause == 0){ printf("身長が不正\n"); }else if(cause == 1){ printf("体重が不正\n"); } printf(“エラー\n"); } int main(void){ doubleheight,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; } 本来であればcauseの取りうる値を マクロにすべきですが、スペースがないので お許しください・・・ 第5章 関数の作成
引数(4) 引数はコピーされた値に過ぎない → 呼び出し先関数内で引数を変更しても 呼び出し元の変数は一切影響を受けない #include <stdio.h> void a(int b){ b=1; } int main(void){ int c=0; a(c); printf("%d\n",c); return 0; } main関数 a関数 0 int c = 0; Cには 影響しない c 0 0 a(c); b c 1 0 b = 1; b c 第5章 関数の作成
return文(1) showErrorMessage関数ですが、 引数が0,1以外の時は関数を強制終了させたいですね 値を返さない関数では return文を使って関数を強制終了できます 文法 return; では先ほどのプログラムを改造しましょう 第5章 関数の作成
return文(2) #include <stdio.h> voidshowErrorMessage(int cause){ if(cause == 0){ printf("身長が不正\n"); }else if(cause == 1){ printf("体重が不正\n"); }else{ return; } printf(“エラー\n"); } int main(void){ doubleheight,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章 関数の作成
値を返す関数(1) #include <stdio.h> intmain(void){ intn, m, nPm, mPm, i, j; printf("n "); scanf("%d",&n); printf("m "); scanf("%d",&m); nPm=1; mPm=1; 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; } 左は を求めるプログラム より、 を求める関数を作れば すっきり書けるのではないか 同じような処理を複数回書くのは プログラミングでは嫌われる 第5章 関数の作成
値を返す関数(2) 改行は普通しない 値を返さない、引数のある関数の定義 文法戻り値データ型関数名(引数1のデータ型 引数1の名前, 引数2のデータ型 引数2の名前, ・・・・){ 関数が呼び出時に実行する文1; 文2;・・・ } 引数がない場合は void また、値を返す文も必要ですね。 return文が、値を返し、関数を強制終了します 文法 return戻り値; 値を返さない関数の時とは違い、 値を返すという役割があるので注意しよう! 第5章 関数の作成
値を返す関数(3) #include <stdio.h> int permutation(intn,int m){ inti , 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章 関数の作成
関数の使い道 • プログラムの改良をしやすくする • 同じような文の繰り返しを避ける がありました、他に • 処理のまとまりを分かりやすくする int main(void){ ・・・・ while(true){ ・・・・ ・・・・ ・・・・ ・・・・ ・・・・ } ・・・・ return 0; } シューティングゲームの例 このへんで初期化処理 int main(void){ init(); while(true){ move(); collisionDetection(); draw(); } dispose(); return 0; } このへんで敵を動かす このへんであたり判定 このへんで描画処理 このへんで終了処理 分かりやすい 第5章 関数の作成
プロトタイプ宣言(1) • 関数は使用する場所より上に書かなければならない 関数Aから関数Bを呼び出し、 関数Bから関数Aを呼び出す場合どうする? ここで登場するのがプロトタイプ宣言 プロトタイプ宣言とは 先に関数が存在することだけを宣言する 文法(値を返す関数) 戻り値データ型関数名(引数1データ型,引数2データ型,……) 文法(値を返さない関数) void関数名(引数1データ型,引数2データ型,……) これが使用場所より上にあれば、 関数の実体が下にあっても使用可能 第5章 関数の作成
プロトタイプ宣言(2) #include <stdio.h> 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; } permutation関数が来ることを予告 プロトタイプ宣言 さえ上にあれば使用可 int permutation(intn,int m){ inti , result; i = n; result = 1; for(i = n;i > n - m;i--){ result *= i; } return result; } 第5章 関数の作成
main関数 • 0プログラムが正常終了した • 1 プログラムが異常終了した main関数とはプログラム起動時に呼び出される関数 だから、プログラムはint mainからスタートするのですね 戻り値はint型で以下のように決められています 引数も指定できますが、ここでは説明しません 第5章 関数の作成
ここまでのまとめ • 値を返さない関数 • 値を返さない関数はreturnで強制終了 • 値を返す関数 • 値を返す関数returnで値を返せる • 引数に代入しても呼び出し元の変数には反映されない • プロトタイプ宣言をすれば下にある関数も呼び出せる void 関数名(データ型 引数名 ,データ型 引数…){ } 戻り値データ型関数名(データ型 引数名,データ型 引数…){ } 第5章 関数の作成
練習問題 問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章 関数の作成
ブロック • ブロックとは{}で囲まれた文の集合のこと • {}は複数の文を1文とみなすことができる • 通常if文など制御文の後には1文しか置けない if( num == 0 ) return 0; if( num == 0 ){ printf("入力が不正です"); return 0; } 1文だけなら {}不要 2文を1文とみなすために {}を付ける ifの後は1文しかこれない 第5章 関数の作成
変数のスコープ ブロック内で宣言された変数は、 そのブロック内のみで使用することが可能 #include <stdio.h> voidfunc(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章 関数の作成
変数の寿命(1) 次のプログラムを実行しよう #include <stdio.h> voidshowZero(boolisInit){ 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章 関数の作成
変数の寿命(2) しかし一回目にshowZero関数を呼び出したときに 確かにzeroに0を代入しました 実はブロック内で宣言された変数の値は ブロックが終了した地点で破壊されます もう一度その部分を通っても跡形もありません 一回目にzeroに0を代入しても、 二回目にはその値は破壊されています そこで出てくるのがグローバル変数 第5章 関数の作成
グローバル変数(1) グローバル変数の特徴 • スコープファイル内全て • 寿命プログラムが終了するまで 文法データ型変数名; 文法データ型変数名 =初期値; 書く場所 • 使う場所より上 • 関数の外 ※逆にブロック内の変数をローカル変数と呼ぶ 第5章 関数の作成
グローバル変数(2) 先ほどのプログラムをこのように修正すればうまく動くはずです #include <stdio.h> int zero; voidshowZero(boolisInit){ if( isInit ) zero = 0; printf("%d\n“,zero); } int main(void){ showZero(true); showZero(false); showZero(false); showZero(false); return 0; } グローバル変数があれば 関数にアクセスしたことがあるかも 取得できます。 その機能があれば 引数はいらないですね 第5章 関数の作成
グローバル変数(3) #include <stdio.h> int zero; boolisCalledshowZero = false; voidshowZero(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章 関数の作成
forと変数 forの初期化の場所に変数宣言を書くことができます。 この変数は・・・ スコープfor文ブロック内 + 評価式 + 毎回実行される文 寿命for文終了まで です。 #include<stdio.h> int main(void){ for(inti = 0 ; i <= 10 ; i++){ printf("%d\n", i ); } return 0; } スコープ ここまで寿命 変数のスコープを極力狭くするときれいなプログラムを書ける → このプログラムは最低限のスコープしかない → 今までの方法よりも見やすくなる可能性が高い 第5章 関数の作成
ここまでのまとめ • ローカル変数の寿命はブロックの終了まで • ローカル変数のスコープはブロック内 • グローバル変数の寿命はプログラム終了まで • グローバル変数のスコープはファイル全体 • グローバル変数の宣言は関数の外 第5章 関数の作成
練習問題 問1 nからmまでのランダム値を返す関数を作れ。 ただし、最初に関数を読んだときのみシードの指定をせよ 問2 1回目その関数を呼び出したときには1を、 2回目その関数を呼び出したときには2を、 3回目その関数を呼び出したときには3を、 n回目その関数を呼び出したときにはnを 返す関数を作成せよ 第5章 関数の作成