こんにちは､ そしてお帰りなさい｡
この講義では､ cで関数を定義することについてお話したいと思います｡
関数を定義する｡
私たちは機能の重要性を知っています｡
メリットは理解しています｡
プログラムでは､ 関数を使いたいものです｡
ですから､ cでプロ関数を作るときは､ 関数の定義の1行目に関数ヘッダを指定する必要があります｡
今回は､ 関数の定義について､ また関数の実装についてお話します｡
関数の実装は問題解決に過ぎず､ 普通のcコードです｡
関数を定義することは､ その関数が存在することを意味します｡
関数を定義する場合､ まず関数ヘッダを作成し､
その後に中括弧で関数のブロックコードを開始します｡
つまり､ if文の中で中括弧を使うときと同じように､
forループの中で中括弧を使うときと同じように､ 関数では中括弧が必要なことを除けば､ 中括弧を使うことができるのです｡
排除することはできない｡
そして､ 常にその関数のコードの開始を意味する｡
開始と終了の中括弧の間にあるものは､ 関数のコードです｡
タスクを実行する場所です｡
つまり､ 中括弧の間を関数本体と呼び､ 関数の最初の行を関数ヘッダと呼びます｡
関数ヘッダーは､ 関数の名前､ 関数のパラメータを定義します｡
これからパラメータと引数について詳しく説明します｡
でも今は､ パラメーターというものがあることだけは知っておいてください｡
そして､ パラメータはデータの受け渡しをするための値の数や種類を指定するもので､
パラメータはデータの受け渡しに関係するものです｡
この関数が返す値の型｡
関数はデータを返すことができると述べました｡
つまり､ 関数ヘッダーには､ 関数の名前､
関数が受け取ることができるデータ､ 関数が返すデータが記載されることになります｡
ここでは､ 渡すことのできるデータや返されるデータに注目するのではなく､
単純な関数を作ることについて話をします｡
関数本体には通常のコードが含まれており､ 関数を呼び出すとそのコードが実行されます｡
また､ 関数本体も引数として渡された値にアクセスすることができる｡
つまり､ そこに渡したデータは､ 関数本体の中で活用できるわけです｡
どんなものかというと､ この､ 関数のヘッダーです｡
関数ヘッダは最初の単語を持つ｡
そして､ 関数ヘッダーは､ 関数から返されるデータの種類を表すキーワードです｡
そこにある次のキーワードは､ 関数の名前である｡
そして､ 左の括弧とカンマで区切られたパラメータがあり､
これが関数に渡されるデータになります｡
そして､ 関数ヘッダを終了させるために右の括弧があります｡
次に､ 関数の実装の開始を意味する中括弧があります｡
そして､ あるタスクを行うために､ いくつものステートメントを持っています｡
先ほども言いましたが､ 機能は小さくしてもらいたいものです｡
まとまりを求めるのです｡
あちこちに出没するのは嫌なのでしょう｡
彼らは一点集中で､ 何か特定のことをやりたがる｡
入力される関数があれば､ 入力を読み込むと言ったように｡
出力されない｡
仕分けをしないとか､ 何もしないとか｡
sortという名前の関数があれば､ それはデータをソートします｡
インプットされないんです｡
機能にはまとまりと小回りが必要です｡
そういう機能の方がデバッグしやすいので､ まとまったものにしましょう｡
そして､ 右の中括弧は､ 関数の実装を終了させるものです｡
これがcの関数の姿である｡
では､ 関数名に注目してみましょう｡ パラメータと戻り値の型については､
後で説明します｡
関数の再定義の最初の行は､ 先ほど述べたように､ コンパイラに左から右へ順番に3つのことを伝えています｡
返される値の型､ 関数名､ そしてその関数が受け取るデータ｡
これは要約すると､ 大事なことなんです｡
関数名に関しては､ 意味のある関数名を選びたいものです｡
これは､ 意味のある変数名を選ぶのと同じくらい重要なことです｡
プログラムの可読性に大きく影響しますので､ 関数にxやyという名前をつけないようにしましょう｡
何をするかで関数の名前を決めます｡
2つの数字を足すのであれば､ addと名付ける｡
2つの数字を引き算するものなら､ subtractと名付ける｡
この例では､ 次のような簡単な関数があります｡
空白を気にせず､ こちらの名前を気にしてください｡
print messageという関数があります｡
そして､ 関数の開始を意味する中括弧が見え､
さらにいくつかの実装があります｡
そして､ この実装は､ 文字列､ テキストを表示する以上のことはしていません｡
そして､ 右の中括弧があります｡
これは非常にシンプルな関数で､ 大したことはできませんが､ 出力を表示します｡
このプリントメッセージの例では､ 最初の行で､
関数が値を返さないことをコンパイラに伝えています｡
それがキーワードのヴォイドです｡
繰り返しになりますが､ そこにこだわる必要はないのですが､ そういうことなのです｡
次に名前です｡
そして､ 名前が引数になってからは､ パラメータと呼ばれます｡
また､ データを渡さない場合は､ 再びvoidというキーワードを使用します｡
関数本体の文は無くても良いが､ 中括弧は常に存在させなければならない｡
つまり､ 実際には実装がなくても､ 関数のためのブレースがあればいいわけです｡
関数本体に文がない場合､ 戻り値の型はvoidでなければならない｡
そしてまた､ 後で焦点を当てますが､ 実装がないので､ この関数は何もしません｡
ボディが空っぽの関数を定義しても､ 何も達成できていなくても役に立つことがあるのです｡
通常､ 非常に大規模なプログラムのテスト段階で行うことが多いですね｡
これにより､ ある特定の機能だけが実際に動作している状態でプログラムを実行することができ､
より良いテストやエラーの切り分けができるようになります｡
関数本体のテストに実装することも可能ですが､
コメントアウトするだけです｡
そして､ このような分割統治を行うことができるのです｡
テストを重ねていくうちに､ 関数本体に実装を入れることができるようになります｡
一歩一歩の積み重ねがあるからこそ､ 仕事ができるのです｡
常に小さな塊で作業し､ コードを追加し､ そのコードをテストし､ 次のステップに進み､
最終的には全体の実装が完了し､ すべての機能が実装されることになるのです｡
ですから､ コードがない､ あるいはコメントアウトされた関数本体を持つというユースケースは存在します｡
ネーミング機能｡
関数の名前は､ 任意の法的名称を使用することができます｡
変数に名前をつけるときと同じように､
予約語は使えません｡
つまり､ これらはデータ型を表すので､ enter doublesなどの言語のキーワードになります｡
Sizeofは演算子です｡
に使いたいとか､ もしもの時に使いたいとか｡
また､ 同じ名前の関数を2つ持つこともできません｡
そして､ これは関数のオーバーロードとも呼ばれている､ ようなものです｡
しかし､ 同じ名前の関数を2つ持つことはできません｡ cではオーバーロードがサポートされていないのです｡
最後に､ 標準ライブラリ関数にある関数と同じ名前にすることができます｡
技術的には､ そのライブラリインクルードヘッダーファイルをインクルードしなければ､ 持つことができるかもしれません｡
しかし､ 実際にそのライブラリ関数を使おうとすると､ コンパイラは「同じ名前の関数が2つある」と文句を言うだろう｡
ですから､ 標準ライブラリの関数と同じ名前をつけないようにしましょう｡
リーガルネームは変数と同じで､ 最初に文字と数字を並べたものです｡
最初の文字は文字でなければならず､ 文字はアンダースコアでもよい｡
関数の最初に数字や変な文字を入れてはいけません｡
選択する名前は､ その機能が何をするのかに関連する､ 意味のあるものでなければなりません｡
これはすでに言ったことですが､ 強調するために繰り返します｡ 意味のある名前はとても重要です｡
関数名や変数名も､ 複数の単語からなるものを定義することが多いでしょう｡
では次に､ コーディングスタイルだけの話を少ししたいと思います｡
私がコードを書くときに､ キャメルケースを使うことにお気づきだろうか｡
変数や関数の名前の書き方には､ 3つの方法があります｡
関数名の各単語を下線文字で区切るのも､
ひとつの方法です｡
それぞれの単語の最初の文字を大文字にしてもいいし､ 最初の単語の後の単語を大文字にしてもいいし､
それでも一緒にストラムするんです｡
間にスペースを入れてはいけないと覚えておきましょう｡
これはあくまでネーミングのアプローチで､
読みやすくするために何らかの慣習に従うことが重要だからです｡
ただ､ 5種類の単語で長い単語を持つこと｡
ですから､ 入力が有効かどうかをチェックするような関数の場合､ すべて小文字にしたくはないでしょう｡
読んでいて､ どこに文字があるのか特定するのは本当に難しいでしょう｡
キャメルケースは､ 多くの単語のどこに単語があるのかを識別するためのもので､
その機能が何をやっているのかがわかりやすいわけです｡
アンダースコアも単語を区切る方法のひとつです｡
また､ 各単語の最初の文字を大文字にすることも､ 分離の方法のひとつです｡
読みやすくするためには､ 関数内の単語を区切ることができることが重要です｡
普段はキャメルケースを使用しています｡
そして､ これらの規約のうちのひとつを使う場合､
すべてのことに使いたい｡ 変数の命名に使いたいし､
関数名にも使いたい｡
通常は､ この一般的なコーディング規約に従うだけで､ より読みやすくなります｡
機能のプロトタイプ｡
関数の定義についてお話しました｡
関数を宣言するのはどうだろう｡
なぜ関数を宣言する必要があるのか､ 関数を定義するときに宣言しているのではないのか､
と言われるかもしれません｡
しかし､ 関数を宣言するという概念もある｡
そして､ 実装される前､ 定義される前の関数を使いたい場合に必要です｡
つまり､ 関数プロトタイプとは､ 関数を定義する文のことである｡
これは､ コンパイラに基本的に関数の詳細をすべて伝えるものです｡
名前､ 戻り値の型､ 各パラメータの型が定義されています｡
だから､ 関数のヘッダーのように見えるのです｡
関数の外部仕様をすべて提供するもので､ 主にシグネチャという言葉を聞いたことがあると思いますが､
関数のシグネチャのことを指します｡
返されるデータ､ 受け取るデータ､ 関数そのものに関する全てを指定しているのです｡
関数のプロトタイプは､ 関数のヘッダーを書くのと同じように書きたいのですが､
唯一違うのは､ 最後にセミコロンを付けることです｡
そして､ これは関数を宣言しているだけです｡
これは､ 関数が存在すると言っているのです｡
関数プロトタイプは､ 関数を呼び出す各ポイントで､ コンパイラが適切な命令を生成することを可能にします｡
また､ 各呼び出しにおいて､ 関数が正しく使用されているかどうかもチェックします｡
関数ヘッダを提供するのは､ 警告や関数宣言がなくても関数を呼び出せるようにするためです｡
つまり､ 本当にコードの順番だけなのですが､ その例をすぐにお見せしましょう｡
標準のヘッダーファイルをプログラムにインクルードすると､ ヘッダーファイルは関数プロトタイプを追加します｡
関数プロトタイプは､ 多くの異なるソースファイルで再利用したい場合､ 通常､ ヘッダーファイルに記述します｡
1つのソースファイルにしか使わないのであれば､ ソースファイル内の一番上に置くだけでよいでしょう｡
しかし､ 様々なソースファイルで使用できるprintf関数を使用しているため､
そのヘッダー関数はヘッダーで定義されるか､ その関数宣言がヘッダーで定義されることになるのです｡
そして今回は､ stdio｡ h.
そのため､ そのソースファイルだけがその関数を使用している場合､ 通常はソースファイル内に表示されます｡
そうでない場合は､ 多くのソースファイルで再利用されるのであれば､
ヘッダーファイルに記述します｡
これにより､ 実装をどこに置いたかに関係なく､ ファイル内のどの関数でも､
定義されたそれらの関数を呼び出すことができるようになります｡
関数の実装を置くと､ 呼び出した後､ cが「知らない」と文句を言いそうです｡
なぜなら､ コンパイルするときは､ 一行ずつコンパイルしているからです｡
宣言を先頭に置けば､ 定義される前､ つまり実装される前に関数を呼び出すことができるのです｡
パラメータ名は､ 関数定義で使用したものと同じである必要はありません｡
名前が違っても大丈夫です｡
名前といっても､ それはデータの識別子のことです｡
関数プロトタイプのパラメータ名を変更することができます｡
データ型は同じでなければならないが､ 名前は変えられる｡
プログラムに含まれるすべての関数の宣言を､ ファイルの先頭に記述することは､
常に良い習慣です｡
これにより､ プログラムの設計に一貫性が保たれ､ 万が一､ 実装前にその関数を呼び出すようなことがあっても､
どの段階でもエラーが発生しないようにすることができるのです｡
以下は､ いくつかの関数プロトタイプの例です｡
ファイルの一番上に､ 私たちのインクルードがあります｡
そして､ 平均､ 合計､ データ取得などの関数を宣言しています｡
関数のヘッダーと同じように見えることにお気づきでしょうか｡
戻り値の型､ 関数で定義されているパラメータを持ち､
最後にセミコロンがあります｡
これらの機能に対する実装はありません｡
これはコンパイラに､ この関数はこういう風に定義されていますよ､ と教えているに過ぎないのです｡
これで､ メイン関数の中で､ 平均､ 合計､ データ取得を呼び出すことができるようになりました｡
実際の実装は､ 呼んでいる場所より下にあることもありますしね｡
もし､ ここに関数ヘッダがなければ､ mainの中で呼び出そうとしたとき､
mainルーチンの下で定義されていれば､ エラーや警告が発生することになるのです｡
では､ それをコードブロックで見てみましょう｡
ここでは､ いつものメインルーチンをご紹介します｡
ここで､ 関数を作成したり､ 関数を呼び出したりする場合を考えてみましょう｡
これからaddという関数を呼び出しますが､ この関数はパラメータを受け取りません｡
7行目のmain関数で呼び出しています｡
10行目に実装することになりました｡
void addと言いながら､ 何もしないんです｡
空っぽにしておくだけでいいんです｡
これをコンパイルしようとすると､ このコンパイラは警告を出すだけであることに気がつくでしょう｡
addには暗黙の宣言があると書かれています｡
関数宣言を使えば､ その警告を消すことができます｡
関数の宣言は､ 基本的にコンパイラに「この関数をどこかで使いますよ」と伝えることができますが､
その実装がどこにあるかはわかりません｡
つまり､ ここで void add と言って､ セミコロンを追加すればいいのです｡
これで､ その警告はなくなり､ 一部のコンパイルでは実際にエラーが発生するようになります｡
今､ コンパイルすると､ 警告もエラーも出ません｡
つまり､ 関数宣言はそういうことです｡
機能をどこにでも配置できるようになります｡
もし､ 関数の宣言が一番上になく､ 呼び出す場所の上に実装があった場合､
コンパイラは実際に関数を呼び出そうとする前に､ その関数について知っているので､
エラーは表示されないでしょう｡
だから､ これをビルドすれば､ エラーがないこともわかるはずです｡
先ほど､ 練習のためにすべての関数を先頭で宣言しておくとよいと書きましたが､
これは必要なことだと思います｡
だから､ それを続けてください｡
しかし､ その習慣を脱したとしても､ 関数を呼び出す前に宣言するか､
プログラムに警告やエラーを出したくないので､ 関数のプロトタイプを用意しなければならない｡
このように関数を定義し､ 関数プロトタイプを使用することができます｡
次回の講義では､ パラメータについて説明します｡
関数にデータを渡す方法｡
ありがとうございます｡