marunomaruno-memo

marunomaruno-memo

[C] 外部変数(グローバル変数)の定義(仮定義)

2013年03月21日 | Android
                                                                    2013-03-21
[C] 外部変数(グローバル変数)の定義(仮定義)
================================================================================

何のキーワードもつけない外部変数を 2 つのモジュールで定義すると、片一方は自動的
に extern がついた扱いになる。
ただし、2 つとも初期値を設定すると、リンクで二重定義となりエラー。
これって C の仕様かな。それとも、gcc 固有の拡張か? (gcc 3.4)

どうやら、C 言語の仕様。
仮定義というもの。リンケージ時に仮定義の中のひとつが定義になる。


ヘッダー・ファイルで外部変数を定義するときに、初期値をつけないことにしておけば、
extern をつけたり、つけなくしたりするための条件コンパイルや、マクロ定義は必要なく
なるなあ。


□ global_test.c
---
#include <stdio.h>
#include <stdlib.h>

void func(void);

int global = 1234;    // (1)

int main(void) {
    printf("main: %d\n", global);
    func();
    return EXIT_SUCCESS;
}
---

□ global_test2.c
---
#include <stdio.h>
#include <stdlib.h>

void func(void);

int global;    // (2)

void func(void) {
    printf("func: %d\n", global);
}
---

□ 実行結果
---
main: 1234
func: 1234
---

□ nm コマンドの結果
---
00402000 D _global
---


(1)と(2)の組合せで、nm コマンドの結果(シンボル・タイプ)
    ------------------- ----------- ------------------
    (1)\(2)            int global; int global = 1234;
    ------------------- ----------- ------------------
    int global;               B            D
    int global = 1234;        D           (*)
    ------------------- ----------- ------------------
    (*) リンケージでエラー(multiple definition of `global')

nm コマンドは、指定されたライブラリ内に存在するシンボルのリストを表示する。
シンボル・タイプは以下のとおり。
    T (コードセクション内の普通の定義)
    D (初期化されたデータセクション)
    B (初期化されないデータセクション)
    U (未定義。シンボルはライブラリによって使われているが、ライブラリ内では定義
       されていない)
    W (weak. もしも他のライブラリも同じシンボルを定義している場合、その定義によ
       りオーバーライドされる)

参考: 
Program Library HOWTO > 5. 雑録 > 5.1. nm コマンド
http://archive.linux.or.jp/JF/JFdocs/Program-Library-HOWTO/miscellaneous.html


ついでに、
JIS X3010「プログラム言語C」6.9.2 外部オブジェクト定義 の例1
を示す。

□ global_tes3.c
---
int i1 = 1;           // 定義、外部結合
static int i2 = 2;    // 定義、内部結合
extern int i3 = 3;    // 定義、外部結合
int i4;               // 仮定義、外部結合
static int i5;        // 仮定義、内部結合

int i1;               // 正しい仮定義、前の定義を参照する
//int i2;             // 前に内部結合を持つ定義があるため、結合の不一致が生じ、
                      // "6.2.2 識別子の結合" によって動作は未定義となる
int i3;               // 正しい仮定義、前の定義を参照する
int i4;               // 正しい仮定義、前の定義を参照する
//int i5;             // 前に内部結合を持つ定義があるため、結合の不一致が生じ、
                      // "6.2.2 識別子の結合" によって動作は未定義となる

extern int i1;        // 外部結合を持つ前の定義を参照する
extern int i2;        // 内部結合を持つ前の定義を参照する
extern int i3;        // 外部結合を持つ前の定義を参照する
extern int i4;        // 外部結合を持つ前の定義を参照する
extern int i5;        // 内部結合を持つ前の定義を参照する
---

□ global_test4.c
---
//int i1 = 1;         // 定義、外部結合    ※二重定義になるのでコメントアウト
static int i2 = 2;    // 定義、内部結合
//extern int i3 = 3;  // 定義、外部結合    ※二重定義になるのでコメントアウト
int i4;               // 仮定義、外部結合
static int i5;        // 仮定義、内部結合

int i1;               // 正しい仮定義、前の定義を参照する
//int i2;             // 前に内部結合を持つ定義があるため、結合の不一致が生じ、
                      // "6.2.2 識別子の結合" によって動作は未定義となる
int i3;               // 正しい仮定義、前の定義を参照する
int i4;               // 正しい仮定義、前の定義を参照する
//int i5;             // 前に内部結合を持つ定義があるため、結合の不一致が生じ、
                      // "6.2.2 識別子の結合" によって動作は未定義となる

extern int i1;        // 外部結合を持つ前の定義を参照する
extern int i2;        // 内部結合を持つ前の定義を参照する
extern int i3;        // 外部結合を持つ前の定義を参照する
extern int i4;        // 外部結合を持つ前の定義を参照する
extern int i5;        // 内部結合を持つ前の定義を参照する
---

□ nm コマンドの結果
---
00402004 D _i1
00402008 d _i2
00402000 d _i2
0040200c D _i3
004040b0 B _i4
00404020 b _i5
00404010 b _i5
---

シンボル・タイプで、小文字はそのシンボルがローカルであることを意味し、大文字はそ
のシンボルがグローバル (外部定義) であることを意味する。


                                                                            以上