32章 ファイルの分割

http://www.geocities.jp/ky_webid/c/032.html

extern指定子というのが出てきました。

概念的にはわかるのですが、いまいちこう何というかピンときません。

使ってるうちに分かるようになってくるんでしょうか、ちょっと不安です。

静的グローバル変数

静的グローバル変数は、変数のスコープが、そのソースファイルだけに限定されます。あるまとまった処理を1つのソースファイルにまとめて記述する場合に、その処理内でしか用はないが、ローカル変数にはできないような事情があるときに使います。よって、小さいプログラムではほとんど使う機会はありません。

これも重要そうですね。きっちり覚えておかないと迷う可能性があります。

/* main.c */

int g_num = 500;     /* グローバル変数の定義 */
void print();        /* 関数プロトタイプ */

int main(void){
    print();
    return 0;
}
/* sub.c */
#include <stdio.h>

extern int g_num;   /* extern宣言 */
void print()  /* プロトタイプ宣言はmain.c */
{
    printf( "%d\n", g_num );
}

このように、ヘッダファイルがなくても、グローバル変数や関数を異なるソースファイルからアクセスすることはできます。extern指定子は「宣言は他にある」といっているのですから、このようなことが可能です。試しにextern を消すと、コンパイル時に「g_numの定義が見つからない」というエラーになります。更にもう1つ試してほしいのが、 main.cのg_numの宣言にstaticを付けることです。staticを付けると、静的グローバル変数となり、main.c以外からはアクセスできなくなります。そのため、例えextern付きで宣言してもsub.cから、g_numをアクセスすることはできません。よってこの場合も、「g_numの定義が見つからない」というエラーになります。

ここのくだりは超重要。

externは宣言の後に実際にどこかで値が設定されてないとダメ!

だからmain.cの方の定義をstaticつけてしまうとsub.c的に宣言はされてるけど定義されてないという状態になる。

静的関数

これも静的グローバル変数と同じで関数の有効範囲がソース内に制限される。

使うことあるかも?

といったところで練習問題

問題1

次のプログラムの間違いを指摘して、正しく修正して下さい。

/* main.c */
#include "sub.h"

int main(void){
    getstring();
    putstring();
    return 0;
}
/* sub.c */
#include <stdio.h>
#include "sub.h"

extern char g_str[81];

/* 文字列を受け取る */
void getstring(){
    fgets( g_str, 81, stdin );
}

/* 文字列を表示 */
void putstring(){
    puts( g_str );
}
/* sub.h */
/* グローバル変数 */
extern char g_str[81];

/* 関数プロトタイプ */
void getstring();
void putstring();

正しく動くように修正してみます。

見たところ、g_strが両方ともexternが付いてます。

つまりg_strの宣言のみで定義がされてない常態です。

よってsub.cのexternを取り除いてやれば動くはずです。

答え合わせ

正解でした!

問題2

次のプログラムは正しいでしょうか。正しくなければ修正して下さい。

/* main.c */
#include "main.h"

static char s_str[MAX+1];

int main(void){
    getstring( s_str );
    putstring( s_str );
    return 0;
}
/* sub.c */
#include <stdio.h>
#include "sub.h"

char *g_str = "Input String Is";

/* 文字列を受け取る */
void getstring(char *str){
    fgets( str, MAX+1, stdin );
}

/* 文字列を表示 */
void putstring(char *str){
    printf( "%s %s\n", g_str, str );
}
/* main.h */
/* 記号定数 */
#define MAX (80)    /* 入力される最大文字数 */

/* プロトタイプ宣言 */
void getstring(char *str);
void putstring(char *str);
/* sub.h */
/* グローバル変数 */
extern char *g_str;

問題文がいやらしいですね。正しいか、正しくないかを明示してない。くぅう。

頭がこんがらがってきました。

とりあえずひとつ分かったのはsub.cで使用しているMAXという定数はmain.hで宣言されているので参照できないはずです。

ではどうすればいいのかということでsub.cでmain.hをインクルードすればいい?のかな。ちょっと分からないです。

答え合わせ

間違っています。sub.cからはMAXという記号定数の定義は見えません。修正するには、sub.cにもmain.hのインクルードを追加します。ただ、ソースファイル名とヘッダファイル名が対応するように記述した方が分かりやすいので、main.hに MAXの定義を記述。sub.hにはgetstring関数とputstring関数のプロトタイプ宣言とg_strの宣言を記述するようにした方が良いかも知れません。もちろん、こんな小さいプログラムでは、ファイルが増える分、逆に分かりにくくなるかも知れませんが。

なるほど。

ただ「main.hに MAXの定義を記述」っていのは多分「sub.hに MAXの定義を記述」と言いたかったんじゃないかと把握。

問題3

次のプログラムは正しいでしょうか。正しくなければ修正して下さい。

/* main.c */
#include "main.h"

char g_str[81];

int main(void){
    getstring( g_str, MAX+1 );
    putstring( g_str );

    return 0;
}
/* sub.c */
#include <stdio.h>
#include "sub.h"

/* 文字列を受け取る */
void getstring(char *str, int max){
    fgets( str, max, stdin );
}

/* 文字列を表示 */
void putstring(char *str){
    puts( str );
}
/* main.h */
#include "sub.h"

/* 記号定数 */
#define MAX (80)    /* 入力される最大文字数 */

/* グローバル変数 */
extern char g_str[81];
/* sub.h */
/* プロトタイプ宣言 */
void getstring(char *str, int max);
void putstring(char *str);

さてさっきと同じくいやらしい問題です。

ざっと見ましたがおかしなところは見つかりませんでした。

なのでこれは正しい!・・・はず!

答え合わせ

正解!やった!

何か引っ掛けがあるのかと少しドキドキしてましたが大丈夫でした。良かったです。