###### tags: `C言語` `Programing` `未来大授業`
# 第4回 関数
## 今回使う知識
以下の知識は,知っているものとします
- printf関数
- scanf関数
- 変数
- データ型
- if文
- for文
- 代入演算子
- 算術演算子
- 関係演算子
!!わからないやつは事前に復習しておくこと!!
==
## 関数の役割
### 「関数」って何のためにあるの?
一度作ったプログラムをもう一度使いたいと思った場合,一番簡単なのは「int main(void)」の中をコピペする,という方法です.
しかし,実際の開発現場になると,大量のプログラムを扱います.その中で,一度作ったプログラムをもう一度使いたくなった場合,わけワカメなプログラムになってしまうのは自明です.
**==そこで関数の出番です!==**
一度作成したプログラムをもう一度使うことを**再利用**といい,++関数として再利用++することを**部品化**といいます.
関数を使えば,プログラムを部品化し簡単に再利用できてしまうのです!
### メイン関数
実は,C言語の授業の中で,皆さんは何回も関数を作っています.(最低でも12回は)
プログラムの最初に,毎回こんなの書いてますよね?
```cpp=
int main(void){
}
```
これも関数です.この関数は**main関数**とよばれ,C言語では==main関数が最初に動作==します.
C言語のプログラミングをするときは必ずmain関数が必要です.
## 関数の書き方
関数の書き方は以下の通りです.
```cpp=
データ型 関数名(引数){
}
```
関数は,**引数を持たない関数(=自然関数)** と **引数を持つ関数**に分類されます.
自然関数の場合,引数のところに ”void” と入力します.
引数を持たせる場合,引数のところに「(データ型 変数名)」と入力します.
引数を持つ関数の最後には,main関数中で使用した関数に値を返します.
この値を**戻り値**といい,**==return文==** を用います.
ex) 0からmaxまでの自然数の偶数和を求める関数
```cpp=
int sum(int max){
int sum = 0;
for (int i = 0; i < max; i += 2){
sum += i;
}
return sum;
}
```
## プロトタイプ宣言
基本的に,C言語において,新しい関数を使えるのはその関数よりも後に記述された関数のみです.つまり,
```cpp=
#include <stdio.h>
int main(void){
int a;
scanf("%d", &a);
printf("%dまでの偶数和は%d", a, sum(a));
return 0;
}
int sum(int max){
int sum = 0;
for (int i = 0; i < max; i += 2){
sum += i;
}
return sum;
}
```
てな感じのプログラムを作ると,main関数はsumという関数を知らないので,エラーがでちゃうんですよ.
そこで,プロトタイプ宣言ってのをやってやります.
やり方は簡単で,メイン関数の前にプロトタイプ宣言するだけです.
プロトタイプ宣言は,「関数のデータ型 関数名(引数のデータ型)」という
形です.
ex)
```cpp=
#include <stdio.h>
//ここからがプロトタイプ宣言
int sum(int);
//
int main(void){
int a;
scanf("%d", &a);
printf("%dまでの偶数和は%d", a, sum(a));
return 0;
}
int sum(int max){
int sum = 0;
for (int i = 0; i < max; i += 2){
sum += i;
}
return sum;
}
```
これでエラーが発生しなくなります.
## 今回の課題
### 4-1
ソフトウエア開発販売会社のエヌ社は,同社の主力ソフト「エヌペイント」のライセンス販売価格を以下のように設定している.購入するライセンス数によって1ライセンスあたりの単価が異なり,たとえば,5ライセンス購入する場合は,1ライセンスあたりの単価が600円のため,600×5=3000円が必要な金額となる.
| ライセンス数 | 1ライセンス当たりの値段 |
| -------- | -------- |
| 1~2 | 1000円/1ライセンス |
| 3~4 | 800円/1ライセンス |
| 5~9 | 600円/1ライセンス |
| 10~29| 400円/1ライセンス |
|30以上 | 200円/1ライセンス |
==キーボードから2つの数字 n1, n2 を読み取って==,n1≦i≦n2 の範囲の==すべての整数 i について,i ライセンス購入するのに必要な金額を画面に表示==するプログラムを作成せよ.
プログラム名は「npaint」とすること.
提出ファイルは npaint.c と npaint の2つのファイルとする.
==入力される数字 n1, n2 はいずれも0以上の整数であり、n1≦n2 を満たすと仮定してよい.==
出力フォーマットは,以下の課題実行例にしたがうこと.
課題実行例の赤字は,自分が入力した部分,青字の部分はプログラムが出力する部分である.
==第4回の演習課題では関数の使い方を学んで欲しいので,ライセンス数を int型の引数(値は0以上)で与えたとき,必要な金額の計算を行って int型の値で返す関数を自ら定義して使用すること==
(※たとえば,この関数に引数5を与えると,前述の計算に基づき,3000を返す).関数を使わなくてもppchkallをパスするプログラムを書くことはできるが,こういう場合は関数を使うと便利だということをぜひ身につけて欲しい.
#### 実行結果
[R36xPCxx:~/pp4] b1018xxx% ./npaint
==0
5==
0: 0 Yen.
1: 1000 Yen.
2: 2000 Yen.
3: 2400 Yen.
4: 3200 Yen.
5: 3000 Yen.
[R36xPCxx:~/pp4] b1018xxx% ./npaint
==6
7==
6: 3600 Yen.
7: 4200 Yen.
[R36xPCxx:~/pp4] b1018xxx% ./npaint
==8
14==
8: 4800 Yen.
9: 5400 Yen.
10: 4000 Yen.
11: 4400 Yen.
12: 4800 Yen.
13: 5200 Yen.
14: 5600 Yen.
[R36xPCxx:~/pp4] b1018xxx% ./npaint
==15
15==
15: 6000 Yen.
[R36xPCxx:~/pp4] b1018xxx% ./npaint
==28
32==
28: 11200 Yen.
29: 11600 Yen.
30: 6000 Yen.
31: 6200 Yen.
32: 6400 Yen.
[R36xPCxx:~/pp4] b1018xxx%
#### ヒント
```cpp=
int price(int a){
int price_sum = 0;
for (int i = 0; i < □; i++) {
if (a > □) price_sum += 200;
else if (a > □) price_sum += 400;
else if (a > □) price_sum += 600;
else if (a > □) price_sum += 800;
else price_sum += 1000;
}
return price_sum;
}
```
##### printfで関数を表示する例
```cpp=
printf("結果は%d\n", price(10));
//%dのところにprice(10)の戻り値が入る
```
---
### 4-2
==2つの0以上の整数 x と n を入力すると,x のべき乗を n+1 個出力する( x~0~ から x~n~ までを列挙する)プログラムを作成せよ.==
この際,==整数 a の b乗(a^b^ )を計算する関数 int calc_power(int a, int b) を作成し,利用すること.== この課題については,<math.h> の pow 関数は利用しないこと.
課題作成においては以下の点に注意すること.
プログラム名は「mypower」とすること.
提出ファイルは mypower.c と mypower の2つのファイルとする.
出力フォーマットは,課題実行例にしたがうこと.
int型は32bitしか保持できないため,-231(=-2147483648)~231-1(=2147483647)の間しか正しい値を持てないが,べき乗の計算結果が==int型の最大値・最小値を超えた場合に対するエラー処理については考慮しなくてもよい.==
#### 実行結果
[R36xPCxx:~/pp4] b1018xxx% ./mypower
==10 2==
power(10,0)=1
power(10,1)=10
power(10,2)=100
[R36xPCxx:~/pp4] b1018xxx% ./mypower
==3 4==
power(3,0)=1
power(3,1)=3
power(3,2)=9
power(3,3)=27
power(3,4)=81
[R36xPCxx:~/pp4] b1018xxx% ./mypower
==2 10==
power(2,0)=1
power(2,1)=2
power(2,2)=4
power(2,3)=8
power(2,4)=16
power(2,5)=32
power(2,6)=64
power(2,7)=128
power(2,8)=256
power(2,9)=512
power(2,10)=1024
[R36xPCxx:~/pp4] b1018xxx% ./mypower
==1 1==
power(1,0)=1
power(1,1)=1
[R36xPCxx:~/pp4] b1018xxx% ./mypower
==8 0==
power(8,0)=1
[R36xPCxx:~/pp4] b1018xxx%
#### ヒント
```cpp=
//関数「calc_power(int, int)」はこんな感じ.
int calc_power(int x, int n) {
int ans = □;
for (int i = □; i □ n; i++)
ans = □;
return ans;
}
```
##### printfで関数を表示する例
```cpp=
printf("結果は%d\n", clac_power(2, 10));
//%dのところにclac_power(2, 10)の戻り値が入る
```
---
### 4-3
まず==①学生の数n (nは10以下とする)を入力== する.次に,==②人数分の英語,数学,プログラミングの科目の点数(int型)を入力== する.==③各科目の平均点(ave),最高点(max),最低点(min),および最高点と最低点との差(diff)を計算し,表示== するプログラムを作成せよ.
==④学生の数を入力する際には,"Number of input:" というプロンプトを表示== する.点数は,課題実行例に示すように,学生ごとに,英語,数学,プログラミングの順に入力する.また,==⑤科目ごとに点数を格納するための配列を用意== すること.==⑥計算結果は,英語,数学,プログラミングの順に表示し,科目ごとに,ave,max,min,diff の順番で表示== すること.
この際,==⑦科目の点数が格納された配列を引数として,その科目の平均点を計算する関数 double calc_ave()== ,==⑧その科目の最高点を計算する関数 int calc_max()== ,==⑨その科目の最低点を計算する関数 int calc_min() を作成== し,利用すること.さらに,==⑩その科目の最高点と最低点の差を計算する関数を calc_max() と calc_min() を利用して作成== すること.なお,必要に応じて,その他の引数も指定すること.
課題作成においては以下の点に注意すること.
プログラム名は「calc_emp」とすること.
提出ファイルは calc_emp.c と calc_emp の2つのファイルとする.
出力フォーマットは,課題実行例にしたがうこと.
課題実行例の赤字は,自分が入力した部分,青字の部分はプログラムが出力する部分である.
==⑪各科目の計算結果を出力する際には,課題実行例に示すように,先頭に科目を表すプロンプトを付けること==.(たとえば,英語の場合は先頭に,"eng>>" を付け,その後に空白一文字を入れる.)
==⑫平均点は,小数第2位まで表示すること==.
==⑬平均点,最高点,最低点の後にはカンマと空白一文字を入れること.==
#### 実行結果
[R36xPCxx:~/pp4] b1018xxx% ./calc_emp
Number of input:1
==70 80 90==
eng>> ave:70.00, max:70, min:70, diff:0
math>> ave:80.00, max:80, min:80, diff:0
prog>> ave:90.00, max:90, min:90, diff:0
[R36xPCxx:~/pp4] b1018xxx% ./calc_emp
Number of input:3
==100 70 60
60 80 70
80 60 80==
eng>> ave:80.00, max:100, min:60, diff:40
math>> ave:70.00, max:80, min:60, diff:20
prog>> ave:70.00, max:80, min:60, diff:20
[R36xPCxx:~/pp4] b1018xxx% ./calc_emp
Number of input:3
==75 80 70
80 100 70
95 60 80==
eng>> ave:83.33, max:95, min:75, diff:20
math>> ave:80.00, max:100, min:60, diff:40
prog>> ave:73.33, max:80, min:70, diff:10
[R36xPCxx:~/pp4] b1018xxx% ./calc_emp
#### ヒント
最小値の初期値は...?
テストは100点満点ですよね...?
配列の要素の足し算は,main関数内でやったほうがカンタン
---
### 4-4
まず,1~9までの数字を9個入力し,配列 x[] に格納し,以下の図に示すように,3×3のマス目に順番に数字を入れる.
| x[0] | x[1] | x[2] |
| ---- | ---- | ---- |
| x[3] | x[4] | x[5] |
| x[6] | x[7] | x[8] |
このとき,このマス目の数字の並びが「魔方陣」になっているかどうか判定するプログラムを作成せよ.==マス目の数字の並びが「魔方陣」になっている場合には,"Magic square!" と表示し,そうでない場合は "It is not magic square." と表示すること.==
「魔方陣」は,==ヨコ,タテ,ナナメのどの方向に数字を足しても,その和が等しくなる.(3×3の魔方陣の場合は,8方向それぞれの和を計算し,それらが等しくなることを示せばよい.)==
※本来の魔方陣は同じ数字を2回使わないで作るものだが,この演習問題ではそれは気にしないことにする.
この際,==入力した9個の数字が格納された配列を引数として,課題実行例のように数字を並べて表示する関数 void display_square()を作成し,利用する==こと.また,==3個の数字を引数として,それらの和を計算する関数 int calc_sum() を作成し,利用すること.さらに,8方向分の和を引数として(配列として渡しても良い),それらがすべて等しい時に 1 を,そうでない時に 0 を返す関数 int check_equal() を作成し,利用する==こと.また,必要に応じて,その他の引数も指定すること
#### 実行結果
[R36xPCxx:~/pp4] b1018xxx% ./magic33
==6
1
8
7
5
3
2
9
4==
6 1 8
7 5 3
2 9 4
Magic square!
[R36xPCxx:~/pp4] b1018xxx% ./magic33
==1
2
3
4
5
6
7
8
9==
1 2 3
4 5 6
7 8 9
It is not magic square.
[R36xPCxx:~/pp4] b1018xxx% ./magic33
#### ヒント
足し算を考えるときは,実際に配列を表の形で考えて,添字の規則性を見出し,それをもとにfor文を使う.
int check_equal()は,**足し算の結果が**すべて等しい時に1を返し,一つでも異なるときに0を返す関数です.
---
以上