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