C言語-ファイルの分割
前へ 目次へ 次へ 

5 ファイルの分割とコマンドmake

 プログラムファイルのサイズが大きくなる(行数が多くなる)とコンパイルに要する時間も増えてくる。ほんの1カ所の修正でも、すべてをコンパイルしなければならない。これを解消するために、関数ごとに別のファイルに分割したり、関数を機能ごとに分類して別のファイルに分割することを行う。ただし、1つの関数を2つのファイルに分割することはできない。

 例えば、Cコンパイラのgcc(ver 2.5.8、ファイル総数833)は、約170の.cファイルに分割されている。gccをコンパイルするために

cc  gcc.c ・・・・・

のようにコマンドの後にファイル名を170個も入力するわけではない。もし、入力すると170個すべてコンパイルされるので、分割した意味がない。変更されたファイルだけをコンパイルして新しい実行ファイルを作成するためのコマンドmakeが用意されている。


5.1 ファイルの分割

次の関数work1を作成する。mainでは整数を入力し結果を表示する。[pro5-1.c]

int work(int x);
    返却値 2x2+5x-12の計算結果
 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int work1(int);
    int xx, yy;
    char buf[80];

    printf("x=");
    gets(buf);
    xx = atoi(buf);
    yy = work1(xx);
    printf("Ans = %d\n", yy);
    return(0);
}
int work1(int x)
{
    int y;

    y = 2 * x * x + 5 * x - 12;
    return(y);
}


実行結果

1
Ans = -5


 このプログラムの関数mainと関数work1を単純に2つのファイルに分割すると、次のようになる。

[pro5-1a.c] [pro5-1b.c]
 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
    extern int work1(int);
    int xx, yy;
    char buf[80];

    gets(buf);
    xx = atoi(buf);
    yy = work1(xx);
    printf("Ans = %d\n", yy);
}
 1:
 2:
 3:
 4:
 5:
 6:
 7:
int work1(int x)
{
    int y;

    y = 2 * x * x + 5 * x - 12;
    return(y);
}

 このプログラムの場合、単に関数work1だけが別のファイルに移動している。したがって、分割後は、mainの5行目の関数の宣言(プロトタイプ宣言)にexternを付けるだけである。
 externは外部参照であることを表している。簡単に言い替えると、この場合「関数work1は、どこか別に用意されているので、そちらを参照せよ」ということになる。

 コンパイルは次のようにする。

        MS-DOS                                  UNIX
        > cc  pro5-1a.c  pro5-1b.c              % gcc  pro5-1a.c  pro5-1b.c

 実行ファイルは、UNIXではa.outであるが、パソコンでは最初のファイル名pro5-1a.exeになる。


整数値の2倍と3倍を計算する関数work2を作成し、結果はグローバル変数に格納する。[pro5-2.c]

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
#include <stdio.h>
#include <stdlib.h>

int   yy, zz;    /* 結果格納用 */

void main(void)
{
    void work2(int);
    int xx;
    char buf[80];

    printf("整数値 : ");
    gets(buf);
    xx = atoi(buf);
    work2(xx);
    printf("Ans1 = %d Ans2 = %d\n", yy, zz);
}
void work2(int a)
{
    yy = 2 * a;
    zz = 3 * a;
}


実行結果

整数値 : 10
Ans1 = 20 Ans2 = 30


 このプログラムの関数mainと関数work2を2つのファイルに分割すると、次のようになる。

[pro5-2a.c] [pro5-2b.c]
 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
#include <stdio.h>
#include <stdlib.h>

int yy, zz;

void main(void)
{
    extern void work2(int);
    int xx;
    char buf[80];

    printf("整数値 : ");
    gets(buf);
    xx = atoi(buf);
    work2(xx);
    printf("Ans1 = %d Ans2 = %d\n", yy, zz);
}
 1:
 2:
 3:
 4:
 5:
 6:
extern int yy, zz;
void work2(int a)
{
    yy = 2 * a;
    zz = 3 * a;
}

 関数work2が別のファイルにあるので、関数mainの8行目のプロトタイプ宣言には、externを付ける。
 関数work2のファイルの1行目の宣言がないと変数xxとyyが未宣言のエラーになる。また、externを付けないと再宣言(同じ変数名を再度、宣言)のエラーになる。
グローバル変数xxとyyは、関数work2の結果を格納するための変数であるから一般的には、関数mainの4行目の変数宣言にexternを付け、関数work2の1行目の変数宣言にはexterを付けない。

        関数main                        関数work2
        4: extern int    yy, zz;    1: int     yy, zz;


整数値を2倍する関数work4と、関数work4を利用して4倍する関数work3を作成する。[pro5-3.c]

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
    int work3(int);    /* 関数は、work3とwork4の2つあるが */
    int x, y;          /* mainではwork3だけを使用。 */
    char buf[80];

    printf("整数値 : ");
    gets(buf);
    x = atoi(buf);
    y = work3(x);
    printf("Ans = %d\n", y);
}
int work3(int a)
{
    int work4(int);    /* work3の中でwork4を使用するため。 */
    int z;

    z = work4(a) * 2;
    return(z);
}
int work4(int aa)
{
    int z;

    z = aa * 2;
    return(z);
}

 このプログラムの関数mainと、関数work3、work4を2つのファイルに分割すると、次のようになる。

[pro5-3a.c] [pro5-3b.c]
 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
    extern int work3(int);
    int x, y;
    char buf[80];

    printf("整数値 : ");
    gets(buf);
    x = atoi(buf);
    y = work3(x);
    printf("Ans = %d\n", y);
}
  1:
  2:
  3:
  4:
  5:
  6:
  7:
  8:
  9:
 10:
 11:
 12:
 13:
 14:
 15:
int work3(int a)
{
    int work4(int);
    int z;

    z = work4(a) * 2;
    return(z);
}
int work4(int aa)
{
    int z;

    z = aa * 2;
    return(z);
}

 関数mainで使用している関数work3のプロトタイプ宣言にexternを付ける。関数work3のプロトタイプ宣言は、そのままでよい。これは、関数work4が同じファイル内に存在するからである。

 コンパイルは次のように行う。

        MS-DOS                                  UNIX
        > cc  pro5-3a.c  pro5-3b.c              % gcc  pro5-3a.c  pro5-3b.c

 次に、関数work3と、関数work4を2つのファイルに分割すると、次のようになる。関数mainはそのまま使用する。

[pro5-3c.c] [pro5-3d.c]
1:
2:
3:
4:
5:
6:
7:
8:
int work3(int a)
{
    extern int work4(int);
    int z;

    z = work4(a) * 2;
    return(z);
}
 1:
 2:
 3:
 4:
 5:
 6:
 7:
int work4(int aa)
{
    int z;

    z = aa * 2;
    return(z);
}

 先の分割と同じ理由から関数work3の3行目のプロトタイプ宣言にはexternを付けなければならない。

 コンパイルは次のように行う。

        MS-DOS                          
        > cc  pro5-3a.c  pro5-3c.c prog5-3d.c
        UNIX
        % gcc  pro5-3a.c  pro5-3c.c prog5-3d.c

 関数のプロトタイプ宣言は、変数の宣言と同様に、関数内で宣言すれば関数内だけで有効であり、関数外で宣言すれば宣言位置からファイルの最後まで有効である。ちなみに、関数外でプロトタイプ宣言をすると次のようなプログラムになる。

[pro5-3e.c]

 1:
 2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
#include <stdio.h>
#include <stdlib.h>

int work3(int);    /* ←関数work3とwork4はこの位置から */
int work4(int);    /* ファイルの最後まで、どの位置でも使用できる。 */

void main(void)
{
    int x, y;
    char buf[80];

    printf("整数値 : ");
    gets(buf);
    x = atoi(buf);
    y = work3(x);
    printf("Ans = %d\n", y);
}
int work3(int a)
{
    int z;

    z = work4(a) * 2;
    return(z);
}
int work4(int aa)
{
    int z;

    z = aa * 2;
    return(z);
}

 関数ごとに分割するとどうなるか考えてみよう。


前へ 目次へ 次へ 
Copyright © 2001 Hiroshi Masuda 

 

 

inserted by FC2 system