C言語関係掲示板

過去ログ

No.1036 複数のファイルで同じ変数を共用するには?

[戻る] [ホームページ]
No.13289

複数のファイルで同じ変数を共用するには?
投稿者---senshou(2004/03/23 20:28:14)


いつもお世話になっております。
今回は題意とヘッダの書き方についてお聞きしたいです。

-----main.c-----
#include "sub.h"
int global; //グローバル変数

int func1(void)
{
    /* 定義 */
}

int main(void)
{
    /* 色々処理 */
}
-----main.c-----

-----main.h-----
#ifndef MAIN_H
#define MAIN_H

extern int global; /* ここではいらない? */
extern int func1(void);

#endif
-----main.h-----

-----sub.c-----
int func2(void)
{
    /* 変数globalを使った処理 */
}
-----sub.c-----

-----sub.h-----
#ifndef SUB_H
#define SUB_H

extern int global;  /* ここでこれが必要? */
extern int func2(void); /* 関数もみんなexternが必要? */

#endif
-----sub.h-----


はっきりいって間違いだらけのような気がします。
ここで、ちゃんとした書き方を身に付けたいので色々指摘してください。

No.13292

Re:複数のファイルで同じ変数を共用するには?
投稿者---isshi(2004/03/23 22:13:10)


私は以下のように書きます。

グローバル変数については、main.c で定義して、sub.c でも使う場合は、
--main.c--
int global;  // グローバル定義

--sub.c--
extern int global;  // 外部ファイルで定義されているグローバル変数を使用

のようにします。

関数については、func1() を main.c で定義して、sub.c でも使うなら、
--main.h--
int func1(void);  // プロトタイプ宣言

--main.c--
#include "main.h"

// func1定義
int func1(void){
  ...
}

--sub.c--
#include "main.h"

のようにします。


No.13294

Re:複数のファイルで同じ変数を共用するには?
投稿者---RiSK(2004/03/23 23:19:02)


# 私も extern はあまり使ったことはなく、間違っていたらご指摘願います。

グローバル変数について:
int global;
これは定義(変数の実体)になります。一つだけであるべきです。
extern int global;
これは宣言です。「どこかで定義されている変数をここでも使います」という意味です。使い回すファイルごとに書けます。
以上は、isshi さんが仰ることと同じはずです。


んで、グローバル変数の定義/宣言をヘッダでする方法もあります。
その際、定義と宣言とで extern があるなしの違いしかないので、マクロを使って一つのヘッダにしてしまうと良いそうです。
# 定義用のヘッダと宣言用のヘッダを作るのはナンセンス
/* global.h */
#ifndef GLOBAL_H_INCLUDED
#define GLOBAL_H_INCLUDED

#ifdef  GLOBAL_VALUE_DEFINE
#define GLOBAL
#else
#define GLOBAL  extern
#endif

GLOBAL int global;

void func1(void); /* (1) */

#endif /* GLOBAL_H_INCLUDED */


/* main.c */
#define GLOBAL_VALUE_DEFINE
#include "global.h" /* extern 無し */
#include <stdio.h>

int main(void)
{
  global = 0;
  printf("%d\n", global);

  func1();
  printf("%d\n", global);

  return 0;
}


/* sub.c */
#include "global.h" /* extern 有り */

void func1(void) {
  global = 1;
}
詳細はCプログラミング診断室第5章をご覧ください。


ここで、私からも質問です。global.h の (1) は関数のプロトタイプですが、 GLOBAL をつける必要はあるでしょうか?
つまり、関数のプロトタイプにおいて extern は意味があるかどうかです。 ご教授願います。
# senshou さん、便乗質問でごめんなさいね。
# 題意からは外れていないと思うのでご勘弁を。


No.13297

Re:複数のファイルで同じ変数を共用するには?
投稿者---YuO(2004/03/24 00:16:17)


int global;
これは定義(変数の実体)になります。一つだけであるべきです。


正確には仮定義です。
#以下Cの話。C++は話が変わってきます。

単一の翻訳単位中に定義は一つまでとされていますが,
仮定義であれば複数書いてもvalidです。
さらに,定義があっても仮定義はかけます。

ちなみに,仮定義というのは,
  • ファイル有効範囲
  • 初期化子を使っていない
  • 記憶域クラス指定子を指定していないかstaticを指定している
を満たすオブジェクトの識別子の宣言です。

でもって,複数の翻訳単位中に外部結合を持つ識別子の定義が存在する場合,
その結果は未定義の動作とされています。

典型的には,
  • リンクエラー
  • 一つのオブジェクトに纏められる
  • 複数のオブジェクトが存在したままになり,どのオブジェクトが参照されるかは翻訳単位ごとに異なる
などの結果になると思います。


ちなみに,翻訳単位中に仮定義のみが存在する場合は,
翻訳単位の終わりに=0という初期化子の付いた定義が存在するかのように動作する,とされています。
#つまり,仮定義がある場合は常に定義が存在する。


ここで、私からも質問です。global.h の (1) は関数のプロトタイプですが、 GLOBAL をつける必要はあるでしょうか?
つまり、関数のプロトタイプにおいて extern は意味があるかどうかです。


意味はないです。
というか,記憶域クラス指定子を伴わずに宣言された場合,
externを伴って宣言されたかのように振る舞います。

古いJISからの引用になりますが……。
JIS X 3010-1993 6.1.2.2 識別子の結合に,

 関数の識別子の宣言が記憶域クラス指定子をもたない場合,その結合は,記憶域クラス指定子externを伴って宣言するかのように決定する。
 オブジェクトの識別子の宣言がファイル有効範囲をもち,かつ記憶域クラス指定子をもたない場合,その識別子のもつ結合は,外部結合とする。


とあります。
#JIS X 3010は最新がJIS X 3010:2003。


No.13298

Re:複数のファイルで同じ変数を共用するには?
投稿者---RiSK(2004/03/24 01:33:54)


いろいろ調べましたが私には難しかったので確認です。
int global;
これは定義(変数の実体)になります。一つだけであるべきです。
正確には仮定義です。
「仮定義」という語は初めて聞きました。「仮定義」の定義も YuO さんの説明で分かりました。最終的には「定義」と見なされることもあるのですね。


単一の翻訳単位中に定義は一つまでとされていますが,
仮定義であれば複数書いてもvalidです。
さらに,定義があっても仮定義はかけます。
#ifdef  GLOBAL_VALUE_DEFINE /* #define されているものとする */
#define GLOBAL
#define GLOBAL_VAL(v)  = (v)
#else
#define GLOBAL  extern
#define GLOBAL_VAL(v)  /* */
#endif

GLOBAL int global;
GLOBAL int global GLOBAL_VAL(1); /* 定義あり */
GLOBAL int global; /* 仮定義 複数 */
確かにコンパイル通ります。


でもって,複数の翻訳単位中に外部結合を持つ識別子の定義が存在する場合,
その結果は未定義の動作とされています。
今の例であれば
int global;
が複数のファイルに存在するという意味ですね?
私が「一つだけであるべきです」と書いたのはこのことにあたると思います。
# 曖昧な表現でした。senshou さん、すみません。


典型的には,
...snip...
などの結果になると思います。
VC6SP5 では
リンク中...
sub.obj : error LNK2005: _global はすでに main.obj で定義されています
リンクエラーでした。


意味はないです。
というか,記憶域クラス指定子を伴わずに宣言された場合,
externを伴って宣言されたかのように振る舞います。
よく分かりました。YuO さん、ありがとうございました。


# JIS 等の引用は Web ですか? それとも仕様書として買うのでしょうか?

No.13300

Re:複数のファイルで同じ変数を共用するには?
投稿者---YuO(2004/03/24 09:36:46)


> 「仮定義」という語は初めて聞きました。「仮定義」の定義も YuO さんの説明で分かりました。最終的には「定義」と見なされることもあるのですね。

そういうことになります。

面倒ごとに巻き込まれたくないなら仮定義も定義として扱うのが簡単なんですけどね。
#知られていないですし,C++では仮定義が存在しない(定義扱い)ので。


># JIS 等の引用は Web ですか? それとも仕様書として買うのでしょうか?

仕様書を買いました。
JIS X 3010-1993 (266ページ)が8000円,追補1-1996 (64ページ)が2000円でした。
英文仕様書であれば,ANSIのサイトでINCITS/ISO/IEC 9899が18ドルで売っています。
#INCITSというのはANSIの下請けに近い団体のようです。

最近のJISは,
JIS X 3010:2003 プログラム言語C (474ページ)は13600円+税,
JIS X 3014:2004 プログラム言語C++ (582ページ)は16500円+税,
とデフレを無視した様な高額化が進んでいます。

ちなみに,
ISO/IEC 9899:1999 (538ページ) : PDF/CHF 44,00 (4000円弱),Paper/CHF 340,00 (29000円弱)
ISO/IEC 14882:2003 (757ページ) : CHF 364,00 (30000円強)
なので,紙で買う分には安いとも言えます。
#まぁ,18ドルは2000円程度ですが……。


英文でよければ,
ISO/IEC JTC1/SC22/WG14 / Cあたりを漁るとCのCommittee Draftなんかが見つかりますし,
WG21には無かったのですが,他の場所にはC++のFinal Draftがあったりします。
#ISO/IEC 9899:1999やISO/IEC 14882:1998などがそのままネット上にあるのは公然の秘密。


No.13309

Re:複数のファイルで同じ変数を共用するには?
投稿者---senshou(2004/03/24 18:51:52)


為に成る話ばかりで、本当にありがとうございました。

ここで、いくつか疑問が。
というのも、ヘッダファイルって何を書くものなのでしょうか?

二重インクルードガードを書いたり、関数のプロトタイプを書いたり、
あとは、構造体の定義を書いたり。
あと、C++であれば、クラスなどもここにかくのでしょうか。
また、どっかでexternを付けた変数の宣言をヘッダファイルに書いてあるプログラムを見た記憶があるのですが、
ヘッダファイルに書くべきではないのでしょうか?
あとは、#includeすべきものを、
プログラム本体(.c, .cppなど)に書くのか、
ヘッダに書くのか、両方に書くのか、
それとも好き好きなのか。

ヘッダ自体使ってみようと思ったのも最近なのでまだよく知らない状況です。
色々調べてみたのですが、端的にしか載っていなくて・・・

どうか色々ご指導下さい。

No.13314

Re:複数のファイルで同じ変数を共用するには?
投稿者---YuO(2004/03/24 19:32:40)


>ここで、いくつか疑問が。
>というのも、ヘッダファイルって何を書くものなのでしょうか?

複数の翻訳単位から参照される場合に,最低限必要な物です。

別のところでも書いたことがあるのですが,ヘッダファイルに書く物の集合は
  • 前処理指令
  • 以下のうちの何れかを満たす宣言
    • 記憶クラス指定子としてtypedefを伴う
    • 記憶クラス指定子としてexternを伴い,初期化子を伴わない (関数宣言含む)
    • 関数指定子を伴う (インライン関数の宣言)
    • 型指定子として構造体共用体指定子または列挙型指定子が含まれており,初期化宣言子並びが含まれない (構造体や共用体の宣言や定義)
  • 関数指定子を伴う関数定義 (インライン関数の定義)
  • コメント
になります。

逆に書かない方が良い物の集合は
  • 関数指定子を伴わない関数定義
  • オブジェクトの定義
ということになります。


>二重インクルードガードを書いたり、関数のプロトタイプを書いたり、
>あとは、構造体の定義を書いたり。
>あと、C++であれば、クラスなどもここにかくのでしょうか。

そうです。


>また、どっかでexternを付けた変数の宣言をヘッダファイルに書いてあるプログラムを見た記憶があるのですが、
>ヘッダファイルに書くべきではないのでしょうか?

externを付けた変数の宣言はヘッダファイルで良いです。


>あとは、#includeすべきものを、
>プログラム本体(.c, .cppなど)に書くのか、
>ヘッダに書くのか、両方に書くのか、
>それとも好き好きなのか。

好き好きといえば好き好きです。ただ,
「ヘッダファイルAをインクルードするにはその前にヘッダファイルBをインクルードしておくこと」
というような制限が必要な場合はAがBをインクルードする構造にした方がよいです。


>ヘッダ自体使ってみようと思ったのも最近なのでまだよく知らない状況です。
>色々調べてみたのですが、端的にしか載っていなくて・・・

C++ですがプログラミング言語C++の第3版の第9章が詳しいです。
#ただ,7350円もしますが……。


No.13326

Re:複数のファイルで同じ変数を共用するには?
投稿者---senshou(2004/03/25 00:03:21)


YuOさん
非常に為になりました。
これで、ヘッダファイルがある程度書けるようになりそうです。

>C++ですがプログラミング言語C++の第3版の第9章が詳しいです。
>#ただ,7350円もしますが……。

うーん、少し高いですねぇ。
使えそうなので欲しいのですが、学習には向かないかな?