関数の宣言と定義


さて、宣言と定義 の違いをもう少し見ていきましょう。 まずは、関数について。

関数の定義というのは、その関数がどのような振る舞いか書いたもののことです。 つまり、こんなやつが関数fooの定義です。 つまり普段見ている関数の実体のことです(decl_func0.c)。


double d_max(double a, double b) {
    if(a < b) 
        return b;
    else 
        return a;
}
じゃあ、宣言というのは何かというと、その関数をどのように使えばいいか、 その型情報を記述したものです。 プロトタイプ宣言 とも呼ばれます。
double d_max(double, double);
/* extern double d_max(double, double); と書くのもOK */
/* double d_max(double a, double b); と引数まで書くのもOK */
/* double d_max(); 引数の宣言は行わなくても一応OK */
さて、宣言しないとどんな事があるか試してみましょう。 利用する側(decl_func1.c)は、これだけ。
int main(int argc, char** argv) {
    double val = d_max(1.0, 2.0);
    printf("d_max(1.0, 2.0) = %f\n", val);
    return 0;
}
コンパイルするのは、こんな感じ。
kamada@cygwin% gcc -c decl_func0.c
kamada@cygwin% gcc -c decl_func1.c
kamada@cygwin% gcc -o decl_func decl_func0.o decl_func1.o
で、実行するとこんな感じ
kamada@cygwin% ./decl_func
d_max(1.0, 2.0) = 256.000000   /* 例えばこんな結果がでる */
嫌な結果です。詳しいことは説明しませんが、これは、d_max() という関数が int を返す関数だとおもってコンパイラが処理してしまった結果です。 これだけ短いプログラムだと原因に気がつくのもすぐでしょうが、 大きなプログラムでこの手の間違いがあると悲惨です。で、こんなトラブルを避けるために コンパイラの警告(Warning)レベルを上げましょう。
kamada@cygwin% gcc -Wall -c decl_func1.c
decl_func1.c: In function `main':
decl_func1.c:2: warning: implicit declaration of function `d_max'
decl_func1.c:3: warning: implicit declaration of function `printf'
implicit declaration つまり、暗黙的な宣言がつかわれたよといっています。 明示的(explicit)な宣言がないという警告です。で、これに気づいてプロトタイプ宣言 を行ったのが、decl_func2.cです。 冒頭に
#include <stdio.h>
double d_max(double, double);
の2行が加わりました。実行するとこんな感じ。
kamada@cygwin% gcc -c decl_func2.c
kamada@cygwin% gcc -o decl_func decl_func0.o decl_func2.o
kamada@cygwin% ./decl_func
d_max(1.0, 2.0) = 2.000000
今度の結果は happy です。
とはいえ、プロトタイプ宣言を「利用者側で行う」のは、嫌なものです。 間違えた内容を書いたらそれこそバグを見つけるのは困難になってしまいます。 ということで、「提供者側で」宣言をまとめておきましょう。header file の出番です。 decl_func0.hがそのヘッダファイルです。 外部で利用される関数の宣言などをとりまとめたものです。この場合、関数d_max() のプロトタイプ宣言がはいっています。
#ifndef __DECL_FUNC0_H__
#define __DECL_FUNC0_H__
/* 上の 2 行はおまじない */

double d_max(double, double);

/* 最後の行もおまじない */
#endif /* !define(__DECL_FUNC0__) */
前後におまじないが入っていますが、 その理由を知りたい人はこちらをみましょう。

ということで「利用する側」も、

#include "decl_func0.h"
と書くだけで、プロトタイプ宣言を取り込めるようになりました。

実は、みなさんが普段良く使う

#include <stdio.h>
#include <stdlib.h>
の中身も、このようなプロトタイプ宣言などをまとめたファイルです。詳しいことは、こちら
さて、皆さんが標準ライブラリを使う場合も、プロトタイプ宣言をきちんと取り込む必要が あります。
man 3 printf
(q で終了)などとすることで、その関数の使い方が表示されます。 その中に、どの header file を取り込めば良いかも載っていますので、 見てみましょう。sin, cos などの使い方が分かるはず。

2001.10.29/ Tomio KAMADA: kamada@cs.kobe-u.ac.jp