雑知識
訪問数 1370   最終更新 2009-06-18 (木) 17:29:45

> 分割コンパイルとmake

分割コンパイルとmake その2

さて、もう一度プログラムを見直してみましょう。まず、次のように分けたとします。

             util.c                                     main.c 
#include <stdio.h>                       |
#include <math.h>                        |
                                         |
struct init_struct {                     |
  int n;                                 |
  double start;                          |
  double end;                            |
  double h;                              |
};                                       |
                                         |
struct init_struct init_data;            |
                                         |
double f(double x) {                     | int main() {
  return(sqrt(1.0-(x*x)));               |   int i;
}                                        |   double sum = 0.0;
                                         | 
void initialize() {                      |   initialize();
  init_data.n = 10000;                   |
  init_data.start = 0.0;                 |   for (i=1; i<=init_data.n-2; i++) {
  init_data.end = 1.0;                   |     sum=sum+f((double)i*init_data.h);
  init_data.h =                          |   }
    (init_data.end-init_data.start)/     |
       (double)init_data.n;              |   sum = sum + 0.5*
}                                        |      (f(init_data.start)+f(init_data.end));
                                         |   printf("%20.17f\n",(4.0*sum*init_data.h)); 
                                         | }

まず明らかに、main.cでいろいろな宣言が不足しています。
 1) #include<stdio.h>はmain.cでも必要でしょう。math.hは関数sqrtのためだけなので、util.cの側にだけあれば一応足りるでしょう。
 2) struct init_struct { ... } の構造体の定義は、そのメンバー要素をmain.cの中でも使っているので、main.cでも必要です。コピーしましょう。  3) 次のstruct init_struct init_data という、構造体変数init_dataの宣言ですが、main.cの中で参照しているので、必要ですね。
 4) util.cに置かれた関数 f と initialize は、main.cの中で呼び出しているので、前のページでも触れたとおり、プロトタイプ宣言が必要です。さもないと、main.cの中でfやinitializeの関数の型や引数の数・型が分かりません。

これらを追加したプログラムを作ってみましょう。

             util.c                                     main.c 
#include <stdio.h>                       | #include <stdio.h>
#include <math.h>                        |
                                         |
struct init_struct {                     | struct init_struct {
  int n;                                 |   int n;
  double start;                          |   double start;
  double end;                            |   double end;
  double h;                              |   double h;
};                                       | }
                                         |
struct init_struct init_data;            | struct init_struct init_data;
                                         |
                                         | double f(double x);
                                         | void initialize();
                                         |
double f(double x) {                     | int main() {
  return(sqrt(1.0-(x*x)));               |   int i;
}                                        |   double sum = 0.0;
                                         | 
void initialize() {                      |   initialize();
  init_data.n = 10000;                   |
  init_data.start = 0.0;                 |   for (i=1; i<=init_data.n-2; i++) {
  init_data.end = 1.0;                   |     sum=sum+f((double)i*init_data.h);
  init_data.h =                          |   }
    (init_data.end-init_data.start)/     |
       (double)init_data.n;              |   sum = sum + 0.5*
}                                        |      (f(init_data.start)+f(init_data.end));
                                         |   printf("%20.17f\n",(4.0*sum*init_data.h)); 
                                         | }

さて、今度はどうか?

おそらく、うまくコンパイルできると思います。

ただ、1つ気になることがあります。それは、構造体グローバル変数init_dataの宣言 (struct init_struct init_data;)がmain.cにもutil.cにもあることです。本来、同じ名前のグローバル変数が重複して宣言されれば、それは問題があります。ただ、現在のCでは、エラーになるのは《同じ名前のグローバル変数が重複して宣言されて、かつ両方で初期化されている場合》のようです。つまり、どちらか一方でも初期化していなければ、エラーにはなっていません。

サンプルです。

             util.c                                     main.c 
struct init_struct init_data;            | struct init_struct init_data;

はエラーになりませんし、

             util.c                                     main.c 
struct init_struct init_data={0,0.0,0.0,0.0};  | struct init_struct init_data;

もエラーになりませんが、

             util.c                                     main.c 
struct init_struct init_data={0,0.0,0.0,0.0};   | struct init_struct init_data={0,0.0,0.0,0.0};

だと

util.o:(.bss+0x0): multiple definition of `init_data'
main.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status

といったエラーが出ます。

気持ちの上で正しくするとすれば、一方で(初期化する・しないに関わらず)グローバル変数として宣言し、他方で extern struct init_struct init_data; のようにextern宣言するべきでしょう。

ここで混乱しないで欲しいのは、構造体の形の宣言

struct init_struct {
  int n;
  double start;
  double end;
  double h;
};

の部分は、重複してかまわないということです。ここは、言ってしまえば、型の宣言と同じで、(グローバル)変数の実体 (オブジェクト用語で言うインスタンスに相当するもの)を作ってはいません。あくまで『形』を決めて、それに名前 init_struct (この名前をタグというそうです)を付けているだけです。

気をつけて違いをみて欲しいのですが、

struct init_struct {
  int n;
  double start;
  double end;
  double h;
} init_data;

と書いていません。今見た書き方は、『形』(=タグinit_struct)と、変数の実体(init_data)を両方同時に宣言してしまう書き方です。習うときによくこのように習うのですが、よく見ると2つのことをやっていて、ここのように分割コンパイルを考えるときには、最後の「変数の宣言」の部分が重複して宣言することになったりするわけです。

ところで、この構造体の形の宣言(タグinit_struct)は、同じものを2つのファイルmain.cとutil.cで書かなければなりません。次に、これを避ける工夫を考えてみます。

その次


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-06-18 (木) 17:29:45 (4761d)