本物のC

C の基本:関数


関数

関数はコードセグメントの一部に名前を付けたものなので、当然実行(呼び出し)ができます。

関数の呼び出し時には、単にプログラムカウンタを飛ばすだけではなく引数(argument)としてデータを(呼び出し側が関数へ)渡したり、返り値(return value)としてデータを(関数から呼び出し側へ)返したりできます。

引数
関数
コードの実行
コンソール
ファイル等
返り値
関数の動作

関数は次の様に宣言されます。

返り値の型 関数名(引数1の型 引数1の名前, 引数2の型 引数2の名前, ...);

例えば

long long abs(int a, int b);

int型の引数abを受け取り、long long型の値を返します。

この時点だと宣言だけなので、メモリ上に関数の実体はありません。

unsigned int abs(int a)
{
...
}

として関数を定義する事で、初めてメモリ上にこの関数の実体が作られる訳です。

またこれを呼び出すには

関数名(引数1, 引数2, ...)

と書きます。(この式自体の値が返り値になります。)

スタック

引数や返り値の受け渡しはスタック(stack)にデータを積む事で実現されています。

スタックは書類の山を積む様なもので、所謂 LIFO(Last In First Out)の「最後に入れたものが最初に出てくる」データ構造です。

スタックにデータを積む(プッシュする)
スタックからデータを取り出す(ポップする)

「スタック」はデータ構造に対する一般名詞であって、今の場合のスタックは特に「コールスタック」や「スタックフレーム」とも呼ばれます。

仮想アドレス空間のスタックにはリターンアドレス(return address)や、「C の基本:変数」で説明する「ローカル変数」も積まれます。

リターンアドレスは関数を呼び出した時のプログラムカウンタの位置を記録しておくもので、これにより関数の処理が終わった時どこに処理を戻せばよいかが分かります。

main関数

Cのプログラムは必ずmain関数を含まなければならないのですが、これはプログラムの実行時にカーネルが(リンカが?)main関数から処理を始めるからです。具体的には実行可能ファイルを解釈した後、main関数のロードされた場所へプログラムカウンタを飛ばします。

main関数は

int main(void)
{
...
}

もしくは

int main(int argc, char *argv[])
{
...
}

の形でのみ書く事ができます。(他にも返り値がvoidだったりするものを見掛けますがあまり好ましくありません。「C の詳細:mainの書き方」を参照)

intは「C の基本:型」で説明しますが返り値が整数である事を示していて、これには 0 のとき成功でそれ以外は失敗と言う意味があります。

「失敗」とは「本来のやるべき事ができなかった」程度の意味で、例えば wav ファイルを読み込んで mp3 に変換するプログラムの場合、wav ファイルが見付からなくて「変換されたファイルを出力する」という最終目標が遂げられなかった場合などです。

まぁよく分からなければ取り敢えず 0 でも問題ないでしょう。

二つ目のmain関数の書き方ではコマンドライン引数(オプション)を受け取るのですが、これについては「C の知識:コマンドライン引数」で説明します。