スライド 1

第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