今週は,
キーワード:「関数」「引数」「戻り値」
C言語をはじめとするプログラミング言語の仕様には,繰り返し現れる処理や頻繁に使われる処理を一箇所にまとめ,
効率の良い記述を行う方法が用意されています.
今日の演習で取り上げるこの仕組みは
今までの演習で用いてきたC言語の printf()
, scanf()
は,実は全て関数でした.
これらは「ライブラリ関数」と呼ばれ,C言語のコンパイラ(この演習で用いているのはEmbarcadero (旧Borland) C++ Compiler)
にあらかじめ付属しているものであり,必要な#include
文の記述により,
プログラム中から自由に呼び出すことができる.
そしてmain()
も実は関数である
したがって,
★ 関数は「自分の資産」になります.正しく動く関数を沢山作り貯めよう.
bekijo_test1.cpp 関数なし | bekijo_test2.cpp 関数あり(おすすめ!) |
|
|
ここでは,上の例をもとに整数型の数を2つ受けとり,計算結果を整数型で一つ返す関数を見てみる.
このように,戻り値の型,仮引数の個数や型を事前に厳密に示すことを
戻り値の型 関数名(型 仮引数名1,型 仮引数名2, ...)
{
// ここに処理を記述
return xxx; // xxxが戻り値
}
int wa_keisan(int a, int b); // プロトタイプ宣言が必要 int main(void) { int x = 1, y = 3; int z = wa_keisan(x, y); // 関数の呼び出し .... } int wa_keisan(int a, int b) // 実際の関数の定義 { return a+b; }
#include <stdio.h> の呪文は何だ? ついに謎が明らかに!
本来,プロトタイプ宣言は,その関数の定義が同じソースコード内には無いことを前提としています.
例えば,既に便利に利用している関数,printf()
などは,
その定義は皆さんが作っているソースコードの中にはありませんね?
そのかわり,stdio.h
というヘッダファイルをインクルードしていますが,
この
int wa_keisan(int a, int b) // 定義を呼び出し側(main)よりも先に書く
{
return a+b;
}
int main(void)
{
int x = 1, y = 3;
int z = wa_keisan(x, y); // 関数の呼び出し
....
}
bekijo(a, b)
の部分)から「引数」a, b を伴って関数waに処理が移る.return
文に到達すると,値を返す操作が行われ,
ここで関数の処理が終了する.(return
文以降に記述された処理は実行されない)bekijo()
内の return
で指定された変数 keisan の値)を,main内のwaに代入.【ヒント】関数は型を持つ(この場合はint).returnされる型(=戻り値の型)と同じでなければならない.
【注】処理の流れ,引数(変数)のコピーや,return
による値の戻りをよく理解すること.
関数の役割を考えた時,引数や戻り値が不要な場合が考えられる.
そのような場合は
#include <stdio.h>
// 引数なし,戻り値無し の関数
void moji_hyouji(void )
{
printf("単なる文字の表示です.¥n");
return; // 戻り値「なし」なので,returnのみ記述する.この場合は省略可
}
// 引数あり,戻り値無し の関数
void wano_hyouji(int a, int b)
{
int x = a + b;
printf("二つの数の和は,%dです.¥n", x);
return; // 戻り値「なし」なので,returnのみ記述する.この場合は省略可
}
// 引数あり,戻り値あり の関数
int wano_keisan(int a, int b)
{
int x = a + b;
return x; /* これは絶対必要!
}
int main(void)
{
printf("一つ目の数を入力してください:");
int a=0; scanf("%d", &a);
printf("二つ目の数を入力してください:");
int b=0; scanf("%d", &b);
moji_hyouji();
wano_hyouji(a, b);
int sum = wano_keisan(a, b);
printf("合計は %d です.¥n", sum);
}
まずは上記のような構造になりますよね? まずは main() 関数.これが無ければ始まりません. そこから napier(int n)関数を呼び出しましょう.引数は.マクローリン展開の階数nです. napier関数の中でマクローリン展開をしますから,そこでべき乗を利用できる必要があります.中身が空のプログラムの「器」をつくる #include <stdio.h> float bekijo(int n) { // nはべき乗の階数 // べき乗の計算をここでする. return 0; } float napier(int n) { // nは展開する数 // ここにマクローリン展開級数を書く. // つまりここからべき乗が利用できる必要がある. return 0; } int main(void) { // ... }
べき乗の関数とそれをチェックするプログラム #include <stdio.h> float bekijo(int n) { float x = 1.0; if(n<2) return x; for(int i=2; i<=n; i++) { x *= (n-i+2); } return x; } float napier(int n) { // まずはここは触らずに置いておく return 0; } int main(void) { // べき乗関数のチェックをするためのメイン関数 for(int i=0; i<7; i++) { printf("%d! = %f\n", i, bekijo(i)); } }
いい感じですね!上記のチェックプログラムの実行結果 0! = 1.000000 1! = 1.000000 2! = 2.000000 3! = 6.000000 4! = 24.000000 5! = 120.000000 6! = 720.000000
ネイピア数を求めるマクローリン展開級数部分とチェック用メイン関数 float napier(int n) { float e = 0; for(int i=0; i<=n; i++) { e += 1.0/bekijo(i); //公式通り.べき乗がちゃんとできていればここが簡単になる. } return e; } int main(void) { for(int i=0; i<7; i++) { printf("%d! = %f, e = %f\n", i, bekijo(i), napier(i)); } }
n=6にすると,小数点以下3桁まで合うことがわかります!上記のチェックプログラムの実行結果 0! = 1.000000, e = 1.000000 1! = 1.000000, e = 2.000000 2! = 2.000000, e = 2.500000 3! = 6.000000, e = 2.666667 4! = 24.000000, e = 2.708333 5! = 120.000000, e = 2.716667 6! = 720.000000, e = 2.718056
問題で指定された入出力を持つプログラム #include <stdio.h> float bekijo(int n) { float x = 1.0; if(n<2) return x; for(int i=2; i<=n; i++) { x *= (n-i+2); } return x; } float napier(int n) { float e = 0; for(int i=0; i<=n; i++) { e += 1.0/bekijo(i); } return e; } int main(void) {// 問題で要求された入出力を持つメイン関数 // for(int i=0; i<7; i++) { // printf("%d! = %f, e = %f\n", i, bekijo(i), napier(i)); // }// これまでのチェック用のメインはコメントで残しておくことをオススメします. int n = 0; scanf("%d",&n); // 階数を入力 printf("%.6f\n", napier(n)); // 小数点以下6桁でネイピア数を出力 }
最終的なプログラムを実行してみた様子 $ ./napier 2 2.500000 $ ./napier 4 2.708333 $ ./napier 6 2.718056 $ ./napier 8 2.718279 $ ./napier 10 2.718282 $
// 関数を使っていないコード. int main(void) { printf("%dねん %dくみ %dばん,", 1, 1, 1); printf("なまえ:%s,", "めいじ たろう"); printf("とくいかもく:%s", "さんすう"); printf("\n"); printf("%dねん %dくみ %dばん,", 1, 2, 2); printf("なまえ:%s,", "めいだい じろう"); printf("得意科目:%s", "こくご"); printf("\n"); printf("%d年 %d組 %d番,", 1, 3, 3); printf("氏名:%s,", "しこん さぶろう"); printf("かもく:%s", "りか"); printf("\n"); printf("%d年 %d組 %d番,", 1, 4, 4); printf("氏名:%s,", "まえへ すすむ"); printf("得意科目:%s", "しゃかい"); printf("\n"); }
// 関数を用いたコード void meibo_print(引数リスト) { // ここを編集 } int main(void) { meibo_print(1,1,1,"めいじ たろう","さんすう”); meibo_print(以下を編集 .... .... }
【ヒント】文字列の引数は,char ...[ ] を使います
【注意】書いたプログラムは,必ずコンパイルし,実行してみて,正しい結果が得られることを確認して下さい. これはすべての課題に共通します.コンパイル出来ないプログラムは採点されません.
#include <stdio.h> #include <math.h> int main(void) { float r = 0.0; printf("radius = "); scanf("%f", &r); float s = M_PI * r * r; printf("area of the circle is %.2f\n", s); }
// 実行例(プログラム名がsum.exeの場合) > sum 1 2 (<-このようにキーボード入力) sum = 3 (<-このように画面に表示する)
【ヒント】main関数の引数は,すべて文字列として渡されてきます. 数値として計算できるようにするためには,文字から数値に変換する必要があります. 以下のような関数を用いて数値に変換できます.
int atoi(char str[]); // 文字列を整数に変換 double atof(char str[]); // 文字列を浮動少数に変換 long atol(char str[]); // 文字列をlong型整数に変換これらの関数を利用するときには,stdlib.hをインクルードする必要がある.
void swap(...)
{
...
}
#if defined(TEST)
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
for(int i=0; i<10; i++){
int x = rand()%100;
int y = rand()%100;
printf("swap実行前:x=%d, y=%d ----> ", x, y);
swap(x, y);
printf("実行後:x=%d, y=%d\n", x, y);
}
}
#endif
自分でやってみて確認しよう!
【ヒント】関数から関数を呼び出すことができる.
【ヒント】和を求める時,ここでは単に3回足し算すれば良いです.