1 明治大学オートメーション研究部 ソフト講習
第6回 関数
講習日:5 月 30 日 文責:機械工学科 3年 菅井 稜1. 関数とは?
関数とは, 繰り返し現れる処理や頻繁に使われる処理を main{}とは別にまとめ, 必要であれば何度でも呼び出せるようにしたものです. 関数を使うことによって 大幅にコード量を減らすことが出来る他, デバッグも有利になります. 今回の講習では, 関数の定義から中身まで自身で作り活用する方法を学びます. 関数は今まで習ったプログラミング概念より少し特殊だけど, 数学で使う関数 f(x)と似た概念だと思うので頑張って理解しましょう!!2. 関数の基本的な形式
#include<stdio.h>int func(int x, int y) { int z; //処理 return z; } int main(void) { int a = 1; int b = 2; int c; c = func(a, b); }
2
<解説>
int
func(
int x
,
int y
)
戻り値の型
関数名(
型 仮引数名1
,
型 仮引数名2
) -(1)
・関数名・仮引数名の命名規則 基本的に規則はなく, 関数名・仮引数名は自由に指定できます. ・返り値の型一致 サンプルの関数の最後の「return~」とは, 「~を戻り値として返します」という意味を持 ちます. 関数の先頭で宣言した”戻り値の型”と“return の後に続く変数の型”は一致しなければ な りません. また, 戻り値をもたない場合は, ”戻り値の型”を”void“とします. (ex1) ・仮引数と実引数 引数とは, func()内に記述された変数や数値のことです. 引数には, 仮引数と実引数があります. 仮引数…関数の定義時-(1)に宣言され, 受け取った値を格納するための引数 実引数…その関数を実際に使用するときに()内に記述する引数 ※実引数が関数の仮引数に引き渡されるとき,その実引数自身ではなく,実引数のコピ ーが引き渡されます. よって, 関数内での引数の変更は実引数に影響しません. ・引数の型一致 仮引数と実引数は, 型と個数を一致させる必要があります. 引数はいくつでも取る事が出来ます。 また, 引数を持たない場合は”仮引数の型(赤字のカッコ内)”を「void」または空白と します. (ex1)ex1) 例えば, 引数を持たない場合, 関数の先頭は void func(void)又は void func()と なります.
3
※関数内で宣言した変数の有効範囲(スコープ)は, 関数内のみとなります.
このプログラムの実行順序を示します. 一応, 読んでおくのもよいかも…
① main 関数内で a,b,c がそれぞれ宣言され, a,b にはそれぞれ数値が代入される. ② func 関数が実行され, 実引数 a.b の値が仮引数 x,y にコピーされる.
③ func 関数内での処理が実行される.
④ func 関数内で定義された z が return される. return された先は c = func(a,b)となっ ているので, c に代入され終了.
気を付けるべきなのは, func(a,b)は”戻り値”であることです. 変数 z が return されるのでは なく, 変数 z の数値が return されます. つまり今回, func(a,b)は変数ではなくただの数値に なります.
4
3. 引数と戻り値をもつ・もたない関数
さて、それでは実際に例を挙げてみてみましょう. まずは簡単な例題. HelloWorld を出力する関数です. <sample1.cpp> HelloWorld なプログラム 関数名は[Hello], 戻り値はなし, 引数もありません. 2 の(1)と照らし合わせてみると, 戻り値がないので”戻り値の型”は[void], 引数 を持たないため仮引数名は空白になっています. (4 行目) 11 行目では, 普通に関数を呼び出しています. Hello 関数が実行され, “HelloWorld!!” がコマンドプロンプトに出力されます. 13~15 行目では, for 文の中で関数を呼び出しています. どんな出力結果になるのか 予想してみましょう.5 次に, 2 つの値を入れると和を戻り値として返す関数を例にとりましょう <sample2.cpp> 和を表示するプログラム 関数名は[func], 戻り値は 1 つ, 引数は 2 つです. 2 の(1)と照らし合わせてみると, 戻り値が int 型のため”戻り値の型”は[int], 引 数も int 型で 2 つのため仮引数名も同じようになっていることが伺えるはずです. (4 行目) ここで注目していただきたいのが, 戻り値の受け取り方です. 21 行目では, c=func(a, b)となっているので戻り値がそのままcに代入されます. c は int 型で定義しているため代入することが出来ます.
次に 24 行目では printf 文の引数に func(a, b)をとっています. func(a, b)は数値 なのでこのような使い方もできます.
6
4. 配列を引数とした関数
前回の演習で配列を扱ったと思います. 実は配列も配列ごと引数として関数に渡せます. 配列を引数とした場合, 関数の宣言(仮引数)には”int x[]” のように書きます. (要素数は 書きません) 関数を実際に使用する時(実引数)には, “sum(x)”のように書きます. ([]は付けません) 以下にプログラムを載せます. このプログラムは, ・受け取った配列の要素にそれぞれ 5 をプラスし 配列の二乗和を返す関数(int jijowa) ・受け取った配列を表示する関数(void print_array) を関数として含みます..7 <出力結果> 出力結果は以下のようになります. 無事, 二乗和を返しつつプリントできていますね!! 関数を実行する前と後で, 配列 a の要素が書き換えられたことがわかると思います. 先ほど, 第 2 章(仮引数と実引数)の※印で引数を関数内で変更しても元の変数に影 響はないと書きました. しかし, 配列の場合は関数内で引数の数値を変更すると main 文に戻ってもそのまま引き継がれます, これは配列の場合, 引数として渡されるものがコピーではなく実体そのものだか らです. (厳密には違います, 知りたかったら “ポインタ 配列 関数” でググって ね!)
5. プロトタイプ宣言
関数の定義は基本的に,その関数を使うよりも前に書いておかなければなりませ ん. プログラムは上から下に読まれるためです. しかし, 沢山の関数をプログラムに用いた場合, main 文がどんどん下に下がって しまい醜いプログラムになってしまいます.プロトタイプ宣言はこれらを解決するために, main 文の上には”int func(int x);” だけを書き, main 文の下に本文を書くという方法を取る事を言います. 最後のセミ コロンを忘れないようにしましょう.
プロトタイプ宣言をすることで,こういう関数がある!ということをコンパイラに 主張することができます
9
6. 値渡しと参照渡し(参考)
関数への値の渡し方は2つあります. 値渡しと参照渡しです. この機能は C++から の搭載ですが bcc コンパイラなら読めるので解説します. ・値渡し 今まで使っていた引数の渡し方. 値渡しの場合, 実引数の値が仮引数へコピーされます. 実引数は仮引数 とは値が同じだけで全く違う変数ということになります. つまり, 仮引数をいく 変更しても実引数は変わりません. ・参照渡し 参照渡しの場合, 仮引数は実引数を”参照”します. 仮引数は, 名前は違えど 実引数そのものを指すことになります. つまり, 仮引数を変更すると実引数も変わ ります. 方法は, 参照渡しにしたい仮引数の型の後ろに「&」を付記するだけです. 例を見てみましょう. (include 省略) void add(int& x) { x = x+43; } int main(void) { int a=34; printf( “%d”, add(a)); } 仮引数xの型が参照型になっているのがわかると思います. x は a を参照しているため, add 関数内で実質 a = a+43 の計算が行われることになり ます. よって, printf 関数で出力される数値は 77 となります. このように, 参照渡しは数値を返さなくても容易に幾つでも数値を変えることが出来 るので便利な一方, 間違えると変わってほしくない変数が勝手にかわってしまうという ことも起こりますので目的に応じて使い分けましょう.10