#include <stdio.h> #include <math.h> int main(void) { double x, y; scanf("%lf", &x); y = rt3(x); printf("%f\n", y); return 0; } double rt3(double a) { double x, new_x; x = a; while (1) { new_x = x - (x*x*x - a) / (3*x*x); if (fabs(new_x - x) < 1e-10) break; x = new_x; } return new_x; } |
rt3はニュートン法を使って与えられた引数の3乗根を計算する 関数だが、このプログラムをコンパイルすると、
proto1.c:15: error: conflicting types for 'rt3' proto1.c:9: error: previous implicit declaration of 'rt3' was here |
のようにエラーになってしまう。これは、C言語が古い設計のせいなのか 上から下に順にコンパイルしていくために発生している。 関数を含んだ部分を正しくコンパイルするためには、 「関数の引数と戻り値の型」が必要である。 このプログラムでは関数rt3の定義がmain よりも後にあるために、mainの中でrt3を呼び出すときに rt3の引数と戻り値の型が分からずに困ってしまうことになる。 C言語では分からない場合は勝手に「int型」と仮定して 先に進むようになっているが、その後実際の定義では引数も戻り値も double型だったことが判明したので、そこでエラーになっている。
これを解決するには、
#include <stdio.h> #include <math.h> double rt3(double a) { double x, new_x; x = a; while (1) { new_x = x - (x*x*x - a) / (3*x*x); if (fabs(new_x - x) < 1e-10) break; x = new_x; } return new_x; } int main(void) { double x, y; scanf("%lf", &x); y = rt3(x); printf("%f\n", y); return 0; } |
のように順序を入れ換えてしまえば簡単だが、
戻り値の型 関数名(引数の型, 引数の型, ...); |
のように書く。上の例では、
double rt3(double); |
をmainの前に挿入して、
#include <stdio.h> #include <math.h> double rt3(double); int main(void ) { double x, y; scanf("%lf", &x); y = rt3(x); printf("%f\n", y); return 0; } double rt3(double a) { double x, new_x; x = a; while (1) { new_x = x - (x*x*x - a) / (3*x*x); if (fabs(new_x - x) < 1e-10) break; x = new_x; } return new_x; } |
のようにすればエラーは無くなる。
上の例題を2つのファイルに分割してみる。
#include <stdio.h> double rt3(double); int main(void) { double x, y; scanf("%lf", &x); y = rt3(x); printf("%f\n", y); return 0; } |
#include <math.h> double rt3(double a) { double x, new_x; x = a; while (1) { new_x = x - (x*x*x - a) / (3*x*x); if (fabs(new_x - x) < 1e-10) break; x = new_x; } return new_x; } |
これをコンパイルするには、次のようにする。
% cc -c main1.c % cc -c rt3.c % cc -o hoge main1.o rt3.o |
1行目でmain1.cをコンパイルしてmain1.oを、 2行目でrt3.cをコンパイルしてrt3.oを生成している。 これは「オブジェクトファイル」と呼ばれるもので、 「実行ファイル」になる前段階の不完全なプログラムである。 ほぼコンパイルは済んでいるが、一部未定義の呼び出し部分があって、 そこが空欄になっているようなものと思えばよい。 3行目でオブジェクトファイルを「リンク」し、実行ファイルhogeを 生成している。「リンク」とは、複数のオブジェクトファイルを結合し、 空欄になっている未解決の呼び出し部分を互いに埋めて完全な実行ファイルに する作業を言う。
このように分割しておくと、編集が楽になるだけでなく、 ある一つのファイルだけ変更した場合は残りのファイルはコンパイル する必要が無いので、コンパイル時間の節約にもなる。
double rt3(double); |
mainは次のようにする。
#include <stdio.h> #include "rt3.h" int main(void) { double x, y; scanf("%lf", &x); y = rt3(x); printf("%f\n", y); return 0; } |
のようにする。
printf.o、scanf.oなどのオブジェクトファイルは /usr/lib/libc.aというファイルの中に入っており、 リンク時に必要ならその中から取り出されて実行ファイルに結合される。
また、/usr/include/math.hにはsqrtやlogの プロトタイプ宣言が記述されている。 sqrt.o、log.oなどは、 /usr/lib/libm.aというファイルの中に入っており、 これは自動的にリンクはされず、リンクするときは-lmを付ける 必要がある。