はじめに
鈴鹿高専生が工学実験やプログラムの授業で死ないために最低限必要であろう関数の知識をまとめました。
対象
基本は鈴鹿高専に所属している人にむけて書いていますが、高専生以外でもc++を勉強中で関数についてなんとなくは使えるけど細かいところまでは理解出来ていない人なども対象です。
※全くプログラミングをしたことがない人は対象外です。(かなり端を折っているため)
注意点
本記事は高専生が普段の授業や課題で死なないために書かれたものであり、実務や言語の細かい使用に頓着したものではありません。
もちろんここがおかしい!だとか、間違っている!といった指摘は勉強の身である私にとってすごく有り難いものですしご指摘いただけるとうれしいですが、あくまで学生の授業内容の補完であるということをご理解ください
今回の記事で説明すること
今回の記事では関数が使えるようになることを目標にします。
具体的には
- 関数の使い方
- 戻り値について
- 仮引数について
この3つについて今回の記事では扱っていきたいと思います。(現時点で戻り値や仮引数の意味がわからなくても問題ありません)
関数の使い方 & 戻り値について
三つに題材をわけておきながらこの2つは同時に説明します。その方がわかりやすいので(笑)
では早速次の関数を見てください
int max(int x, int y){
if(x > y) return x;
else return y;
}
上記の関数の使い方や1行目のint max のint の意味をきちんと説明できますか?
ここをあまり理解していないと自分で関数を作れないし、すさまじい量のエラーを吐き出してします
まずmax関数の使い方は以下の通りです。
int a = 10, b = 20;
cout << max(a, b) << endl;
実行結果
20
具体的な処理としては
max(a, b) が呼ばれる → 関数内で、aとbが比較されbの値がreturnされる → bの値が関数が呼ばれた場所(2行目)に戻ってくる → 出力
という流れです。
大事なところはbの値が戻ってくるというところで戻ってくる値を戻り値といいます。
イメージとしてはmax(a,b)が呼ばれてreturnされたbがmax(a,b)のところに代入されて出力されている、でいいと思います。
だからmax(a, b)は一つの変数と思ってもらってよくて、システムとしてはint a = 10;と同じです。
そして変数には型が必要ですよね?先ほどのaは明示的にintと書いてありましたが、max関数の型は一体何型なのでしょうか。コードを振り返ってみましょう
int max(int x, int y){
if(x > y) return x;
else return y;
}
そうです、1行目に書いてある、int こそがmax関数の型になります。
まとめると
- int maxのような関数はひとつの変数ととらえられる
- maxに代入される値は関数の戻り値でreturn a;のような形で記述される
- 関数の宣言時のint max(int x, int y)のintは戻り値の型を表している。
これで関数の基本的なところは理解出来たと思います。ですが上記の説明ではカバーしていないところがあります。それはこういう関数です。
void swap(int x, int y){
int tmp = x;
x = y;
y = tmp;
}
void型の変数なんてねーよ!!
と、上記の説明だとなってしまうわけです。
ではvoid swapの説明に移っていきたいと思いますが、まずその前に果たして関数とはなんのためにあるのでしょう。今更かよ!という感じですがvoidを理解するうえで大事なステップです。
そもそも基本的に関数とは「手続きをまとめたもの」です。
void swapは引数で与えられた2つの変数の値を交換するものですが、これは関数を呼び出さなくても
tmp = x; x = y; y = tmp;
と3行のコードをかけば事足ります。しかし値を交換する作業は何回も登場するのでそれを何回も書くのは面倒 → じゃあそれをまとめたものを関数として呼び出して楽をしよう、というのがそもそもの関数です。
int maxと同じように関数をひとつの変数と定義するならば
swap = {tmp = x; x = y; y = tmp}
といったようにswapという変数には複数の式が代入されているという風にもいえるかもしれません。
そして、このような変数には当然int型やdouble型のような型が存在しません。だから戻り値の型も存在しないのでvoidで型がないというのを表している、ということです。このことからvoidのことを型無し型と呼んだりします。
そしてint maxなどの戻り値がある関数は最終的に関数に10や20.5のような数が代入されるのでvalue = max(10, 20);
のように数として扱えましたが、void swapなどの戻り値のない関数はあくまで式をまとめただけなので次のように扱います。
int a = 10, b = 20;
swap(a, b);
cout << "a = " << a << endl;
cout << "b = " << b << endl;
実行結果
a = 20
b = 10
まとめると
- void swapのような関数には戻り値がない(手続きをまとめただけ)
- 式をまとめただけなので数としては扱わずコードの中にそのまま埋め込む
以上で戻り値、関数の使い方については終わりです。次の仮引数が終わればある程度関数が使えるようになるのでがんばってください。
仮引数について
まず仮引数とはint max(int x, int y)
のint xやint y のことです
int max(int x, int y){// 左のint x とint yが仮引数
//省略
}
この仮引数は
- max関数を使うには引数が2つ必要だよ
- 2つの引数はどちらもint型だよ
という2つの情報を伝えてくれています。なので2つのint を渡せばこの関数は動きます
int a = max(10, 20)
といった感じですね。
ではmax関数がこのように宣言されていたらどうでしょう。
double dmax(double x, double y){
if(x > y) return x;
else return y;
}
おさらいですが1行目にdouble dmaxと定義されているのでこの関数の戻り値はdouble型です。
そしてさきほどのint maxにあてはめて考えると
- dmax関数を使うには引数が2つ必要だよ
- 引数はどちらもdouble型だよ
ということになります。なのでdmax関数の使い方はdouble a = dmax(10.02, 20.32);
といった感じになります。
ここから少し難しいポインタや参照を交えて話していきますが、身構える必要はありません。今までの内容と基本的に変わりはしないので。
ではまずポインタと参照の宣言の仕方をおさらいしましょう。
int a; //int型の値が入る変数
int* a; //int型のアドレスが入る変数 ※それをポインタと呼ぶ
int& b = a; //int型の変数aの参照
これだけおさえていればとりあえず関数を使うことはできます。(ポインタや参照についての詳しい説明はまた別の記事でします)
ではこれらを使った関数を見ていきます。(関数はかなりテキトーです。)
void sample(int& x, int* y){
//省略
}
どうですか、ここまで記事を読んでくれた皆さんならこの関数の使い方をなんとなくイメージ出来たのでないでしょうか
一つずつひもといていくと、まずvoid型の関数なのでコードにそのまま埋め込んで使います。
そして仮引数を見ると
- sample関数を使うには引数が2つ必要だよ
- 1つめの引数はint型の変数、2つめの引数はint型へのポインタだよ
ということがわかります。使い方としては
int a;
int* b;
sample(a, b);
となります。 ※省略しましたがa, bにはなにかの値が代入されている前提です。
注意!!
参照はint& a = b; (bはint型の変数)
のように使う、つまり他の変数(intやdouble、ポインタ等)と違い右辺がint型の参照ではなくint型の変数となっているため右辺にはint型の参照を与えるのではなく、int型の変数を与える
これはvoid sample(int& x, int* y)が実行される前にx = 第1引数,y = 第2引数
が実行されているため(このことから何型の引数をいくつ渡せばいいのかがわかるんですね!)
全体のまとめ
-
int max()
のintは戻り値の型で、この関数は数として扱える -
void swap()
のvoidは戻り値がないことを表す。この関数は数として扱えず、ただ単に式をまとめたもの -
int max(int x, int y)
のint xなどを仮引数といい引数をいくつ、どの型で渡せばいいのかを表す。
以上のポイントを押さえておけば関数を使うことは出来ると思います!
感想
アウトプットするのは大事だぞ!!!みたいな記事をよく見かけて、実際クラスメイトに説明すると頭の中が整理されて自分自身の理解が深まっていくのを感じていたので、普段からお世話になっているQiitaにアウトプットしようと思いたったはいいものの、やってみるとすごく難しかったです。(笑)記事の投稿は続けていきたいとは思っているのですが、自分が思っていた以上に難しくて挫折しそうです。とりあえずポインタ編とクラス基礎編は書き上げようとは思っているのですがその投稿もいつになるのやら、、、
とりあえず今回はこれで終わりです。読んでくださりありがとうございました。
補足
本記事ではわかりやすさを優先したため、適切でない表現が多々あります。現時点で関数を勉強しらっしゃる皆様は見て頂かなくても構わないのですが、一応補足しておきます。
不適切な表現その1
イメージとしてはmax(a,b)が呼ばれてreturnされたbがmax(a,b)のところに代入されて出力されている、でいいと思います。
だからmax(a, b)は一つの変数と思ってもらってよくて、システムとしてはint a = 10;と同じです。
関数に代入される → int a = 10;と同じシステムと表現は不適切です。
int a = 10;、これは代入ではなく初期化ですが、こうかいた方が後の変数に型が必要、という部分を導きやすく初学者にとって代入と初期化の違いを意識しなければならないことは少ないためこういった表現をしています。
不適切な表現その2
void swap(int x, int y){
int tmp = x;
x = y;
y = x;
}
この関数では値の交換が出来ないのも承知の上で書いています。しかしここでポインタや参照を使ってしまうと著しく難しくなってしまううえ、そこは本記事で説明したいこととは外れるため、あえて動作はしないが説明するには十分であると判断しこのvoid swapを書きました。
また次にポインタ編を書こうと思っていて、そこではvoid swapで説明しようと思っているためその布石といった意味もあります。
不適切な表現その3
int& a;
int* b;
sample(a, b)
参照には代入が出来ないため、必ず初期化しなければならないが、このコードでは初期化されていない。代入と初期化を明確に区別しなければならないひとつの例