こんにちは､ そしてようこそ｡
この講義では､ 関数からデータを返すことについて話したいと思います｡
ここまでで､ 関数とは何か､ なぜ関数が便利なのか､ 関数を定義する方法､
関数にデータを渡す方法などがわかってきました｡
ここで､ 関数からデータを返す方法について説明したいと思いますが､
これも非常に便利です｡
先の例では､ 2つの数値を掛け合わせるとき､ 掛け算の関数が計算結果を端末に表示していた｡
そういえば､ あそこでprintf文がありましたね｡
また､ 先ほどは簡単な機能だけという例もありました｡
関数の中のデータをプリントアウトしただけです｡
実際には複数のことをやっているので､ そこに持ち込むのではなく､
いい加減なことをしたかったのです｡
実際にプリントアウトして､ データが返され､ main関数でデータをプリントアウトしていました｡
計算結果が常に表示されるとは限りません｡
データを返すことができれば､ 関数はより柔軟に､
より汎用的に使用できるようになります｡
そのため､ 関数はcの特定の構文を使ってデータを返すことができます｡
なぜなら､ 多くの例で､ 最後のmain関数がデータを返していることを思い出してください｡
だから､ 私たちも少しは触れているつもりです｡
ただ､ 私たちはその意味をよく理解していないだけなのです｡
ここでもう一度､ 関数の一般的な形式について見てみましょう｡
この関数ヘッダを覚えておいてください｡
最初に定義された言葉であるreturn typeがありました｡
そして､ 名前と､ 括弧の中のカンマで区切られたパラメータを用意しました｡
名前とパラメータはすでに知っている｡
その第一は､ 戻り値の型である｡
return type は､ 関数が返す値のデータ型を指定する｡
関数はあらゆる種類のデータを返すことができる｡
もし､ どのようなデータを返すのかをコンパイラに知らせればいいのです｡
int､ float､ doubleのいずれであっても､ 関数のヘッダーで指定する必要があります｡
戻り値の型｡
ここで見た戻り値の型ですが､ cで正規の型であれば､ 関数が返す値にその型を指定することができます｡
主なものとして､ double､ floatなどを挙げました｡
しかし､ 列挙型やポインターを返すこともできる｡
ポインターの話はしていませんが､ ポインターを返せるようになると､ とても､
柔軟性が増しますね｡
戻り値の型は､ 回避型でもよい｡
このキーワードの空白をよく見かけますね｡
その意味が理解できました｡
Void は､ データが返されないことを意味する｡
関数名の前にvoidとあれば､ 何もデータを返していないことになります｡
パラメータリストの中にvoidがあれば､ 何もデータを渡していないことになります｡
Voidは､ 戻り値の型やパラメータを指定する一般的なキーワードで､
ポインタとしても使用できます｡
しかし､ voidは何も言わないということです｡
また､ 何も入れないのではなく､ voidを入れるのは､
プログラムを読みやすくするためです｡
実は､ 関数にデータを渡すときに､ 渡されるデータがない場合は､
空白の括弧だけにすることができるんだ｡
そこにボイドがあると読みやすいですね｡
リターン型も同じですが､ リターン型はvoidでなければなりません｡
ただ空っぽなだけではダメなんです｡
Void は､ データが返されないことを意味する｡
return文のことです｡
どのようなデータが返されるかを特定する方法については説明しましたが､ 実際にデータを返すにはどうすればよいのでしょうか｡
関数の本体の中には､ 何を返すのかがわかるようなステートメントが必要です｡
なぜなら､ 関数の本体は多くのデータを持つことができることを思い出してください｡
ローカル変数もあれば､ 特定の計算をする関数､ 複数の計算をする関数､
何を返す関数など､ 何でもありです｡
幸いなことに､ returnというキーワードでこの文がある｡
もし､ returnの後に変数名や式がない状態で提供した場合､ それは単に関数を終了していることを意味します｡
ループの切れ目に似ていますね｡
関数から戻っただけでしょ｡
そして､ voidと定義されている関数で提供することができます｡
機能を終了するときが明確になる｡
リターンは関数の一番下の1カ所だけにするのが基本です｡
後で説明しますが､ 関数の中のどこにでもあるわけではありません｡
関数内のどこかで見かけたら､ その関数から飛び出していることを意味します｡
なぜ下にあるのがいいかというと､ 機能を終了するときにとてもわかりやすいからです｡
関数の中でずっとデータを返していても､ 複数回返していると､
いつ返しているのかわからなくなるんです｡
そこで､ 私が関数でやりたいことは､ 変数を作って､ 最後に一度だけ返すことです｡
関数の中で何かを返す必要がある場合､ つまり関数や何かの中の特定のif文で変数を設定し､
それを一番下で返すことにしています｡
つまり､ returnの後に何もない場合､ それは単にデータが返ってこない状態で関数を終了していることを意味します｡
これは本当にあまり使われていないんです｡
void関数で使うだけで､ 値を返さない｡
return文のより一般的な形は､ ある変数やある式を返すことである｡
Xにyを加えたもの､ またはxを返したもの｡
この形式のreturn文は､ 関数ヘッダで指定された戻り値の型と一緒に使用する必要があります｡
もしintを返すなら､ その変数や式が何であれintでなければならない｡
intを返すのであれば､ intに評価されなければならない｡
今､ ダブルを回すとしたら､ ダブルに値を合わせなければならない｡
さまざまな種類のデータを返すことができることを覚えておいてください｡
呼び出したプログラムに返される値は､
式がそう評価されたときの結果である｡
x＋yであれば､ その式の結果を返しているわけですから､ 先ほどの関数宣言と同じ型でなければなりません｡
関数本体にステートメントがあるが､ 値を返さない関数は､
戻り値の型をvoidとしなければならない､ have to｡
それがないなんて､ ありえない｡
それがなく､ データを返さない場合は､ エラーメッセージが表示されます｡
プログラムをコンパイルしたときに､ 戻り値がvoid型の関数が含まれていて､
値を返そうとすると､ コンパイラーエラーになります｡
Voidはデータを返さないことを意味し､ 変数や式を返してはいけない｡
関数がvoidの戻り値型を持たず､ 有効な戻り値型を持つ場合､
コンパイラが指定と異なる型を返そうとしたり､ 後ろに何も付けないでreturn
byしただけの場合は､ エラーメッセージが表示されることになります｡
だから､ ここに注目してください｡
関数宣言の冒頭にあるこれらのキーワードは重要である｡
これは関数の実装に直接影響し､ コンパイルが失敗して正しいデータを返さなかったり､ データを返さなかったり､
データを返すべきでないのにデータを返してしまったりすると､
コンパイルが教えてくれるのです｡
式の結果が､ 関数ヘッダの戻り値の型と異なる型である場合､
cはそれを変換しようとします｡
コンパイラは､ 可能であれば変換しようとします｡
例えば､ floatを返すときに､ Iに2つのintを加えたxを表現した場合､
floatに変換するだけでいいわけです｡
intを返しているときに､ float計算をしている式があれば､
itに変換してくれるのです｡
その場合､ データは失われますが､ 変換はしてくれますよ｡
変換ができないこともあるんです｡
それができない場合は､ コンパイラが出すエラーになります｡
文字列を返すべき時にintを返そうとしたり､
enumを返すべき時にenumに戻そうとすると､
コンパイラに文句を言われます｡
また､ 関数のreturn文は1つだけではありません｡
でも､ できるんです｡
関数全体で複数のリターンを持つことができます｡
関数からあちこちに飛び出すので､ コードを追いかけるのが大変になるだけです｡
戻り値が複数ある場合､ 関数の戻り値が式かその型の値であること､
または変換可能であることを再度確認してください｡
さて､ データを渡してデータを返すという話をしたので､ 関数を呼び出す方法はわかったと思いますが､
呼び出しについてもう2つほど言っておきたいと思います｡
関数を呼び出すには､ 関数名の後に括弧で囲まれた引数を渡せばいいのです｡
関数を呼び出すと､ 括弧の間に指定した引数の値が関数内のパラメータに代入されることになる｡
パラメータは､ データ型を持つ代表的な変数である｡
そして､ その変数を関数本体の任意の側で使用することができます｡
関数が終了するとき､ あるいは特定のコードを実行するとき､ 先ほど述べたように､ 引数として与えられたこれらの値が使われます｡
関数を呼び出すときに指定する引数は､ 関数ヘッダのパラメータと型番や順序が一致している必要があります｡
順序も重要です｡ 強調しませんでしたが､ int x double y と書かれていたら､
まずintを渡し､ 次にdoubleを渡さなければなりません｡
doubleとintで渡せない｡
注文はとてもとても具体的です｡
ですから､ 関数を呼び出すときには､ 関数が関数ヘッダーのパラメータリストで宣言されているのと同じように､
データを引数として渡すようにしてください｡
関数を呼び出して､ 返されたデータを代入すること｡
関数がデータを返す場合､ その関数は等号演算子を使うだけでそのデータを代入でき､
同様にreturnの後でも､ 必要であればローカル変数に格納することができます｡
また､ 式でも動作します｡
つまり､ return x plus y､ return x､ return a constantと言うことができるのです｡
この例は､ 呼び出した後に､ 返されたデータを代入する場合です｡
つまり､ int xが私の関数呼び出しと等しい場合､ その関数が返すものはすべてxに代入されることになります｡
そして､ もし私の関数呼び出しがx＋yを返したら､ x＋yの結果はxに代入されることになる｡
つまり､ equalsの左側に変数を､ 右側に関数の呼び出しを置くだけで､
関数呼び出しから返されるデータを代入することができるのです｡
呼び出し側の関数は､ 呼び出された関数から返された値を認識したり処理したりする必要はありません｡
そこにその課題を持つ必要はないのです｡
関数がデータを返してくれて､ そのデータを気にしないのであれば､ イコールは全く必要なく､
関数を呼び出すだけでいいのです｡
データを返すということは､ それを見る必要があるのだろうが､ それをする必要はないということなので､
あまり意味がないだろう｡
関数を呼び出すだけで､ データを処理しなくても､
コンパイラは文句を言わない｡
関数呼び出しから返されたデータをどのように使うかは､
あなた次第です｡
ここでは､ 関数を呼び出す例と､ 関数からデータを返す例を紹介します｡
この例では､ 2つの数字を掛け合わせる｡
これは以前の例と同じですが､ 以前の例ではデータを返さなかったのです｡
先ほどの例では､ 出力が表示されました｡
この例では､ 何も出力していません｡
データを返して､ その出力をmain関数で表示しているのです｡
ここでは､ 2つの数値の乗算は､ 2つの整数をパラメータとして受け取ります｡
その2つの整数を掛け合わせるのです｡
そして､ この金額や値を表示する代わりに､ returnキーワードを使って､
resultという変数を指定し､ セミコロンで終了させています｡
関数本体の中にreturnキーワードがある場合､ そのデータを返していることになります｡
そして､ それを複数個所持つことができることを忘れないでください｡
しかし､ 返せる値は1つだけで､ 複数の値を返すことはできません｡
ですから､ returnキーワードが複数箇所にある場合は､ その時点で飛び出してしまうので､
if文やループの中でなければなりません｡
リターンの後に別のリターンがある場合､ どちらの場合もリターンが2つあると判断され､
エラーになります｡
いつも結果論に終始しているから､ ダメなんだよ｡
でも､ 帰りのキーワードが重要なんです｡
変数を返すんですね｡
そして､ メイン関数の中で､ 返されたデータはresultという別のローカル変数に代入されているのがわかる｡
ここでは､ 同じ名前を使用していることにお気づきでしょう｡
これは､ 次の講義で説明する関数のスコープ､
ローカル変数とグローバル変数とに関係しています｡
この講義で注意しなければならないのは､ この関数が2つの数値を掛け合わせたデータを返している､
という事実だけです｡
どうやって返すかというと､ returnキーワードの後に式の変数をつけて返すのです｡
また､ 関数ヘッダーの先頭で､ 2つの数字の掛け算が戻り値の型を指定していることに気づく｡
intを返している｡
だから､ intを返さなければならない｡
また､ main関数でデータを代入するとき､ int型に代入していることに気づきましたか？
等号演算子で簡単に代入できます｡
データにこだわらなければ､ 2つの数字を掛け合わせるだけで､
あとは何もしないでも良いのですが｡
関数から返されたデータを取得したら､ 次にprintf関数を使って出力します｡
これにより､ データを取るという機能がよりまとまりました｡
実際に関数の中で結果を表示しているわけではありません｡
結果を表示せず､ ただ2つの数値を掛け合わせ､
その積を割り算したい場合もある｡
渡すデータ､ 返すデータの柔軟性は大きなポイントです｡
これは簡単な例です｡
次回のチャレンジでもっと触れてもらう予定です｡