3.15 スコープ、記憶クラス、リンケージ  (2000.03.12 初版)
 これまで組み込み型の変数や、ユーザ定義型(抽象データ型)の変数であるクラス・
オブジェクトについて扱いました。また変数やオブジェクトを操作する関数について見
てきました。これら変数(オブジェクト)や関数の名前のことを識別子といいます。
 ここでは、識別子の「型」以外のもう1つの属性である、スコープ、記憶クラス、リ
ンケージについてまとめます。

3.15.1 グローバル変数とローカル変数

 変数には、プログラム内で大域的に通用するグローバル変数と、局所的な部分だけ
に通用するローカル変数の2つがあります。C/C++ では、関数の外で宣言された変数
はグローバル変数となります。グローバル変数は、それが宣言された場所からプログラ
ムファイルの終わりまでが通用範囲となり、プログラムの実行時にメモリが割り当てら
れ終了時に消滅します。これに対して、関数内で宣言された変数はローカル変数となり
ます。ローカル変数は、関数の外からはアクセスできません。また、関数が呼ばれたと
きに初期化され、関数が戻るときに消滅します。

 これまでのページでは、2,3のプログラムを除いて、すべてローカル変数だけを扱
いました。例外的に使ったグローバル変数は、const修飾子を付けた変更不能な定数と
してのみ使っています。ローカル変数は、最も基本的な変数概念だからです。変数の値
を他の所から不用意に変更されたり、変数の名前(識別子)が衝突したりすることを避
けるには、変数をできるだけ局所的範囲に限定する必要があります。この態度は、C++
プログラミングでは特に強調されます。

 局所変数の重要性は、コンピュータ科学の初期の頃から認識されていました。最初の
プログラミング言語(高水準言語)であるFORTRAN(Formula Translationの略語、
フォートラン)にはグローバル変数しかありませんでした。すぐ後に生まれたALGOL(
Algorithmic Languageの略語、アルゴル)ではローカル変数を使う言語として設計さ
れます。ALGOLは現在は使われていませんが、その後のC/C++を含むプログラミング言語
に多大な影響を与えました。ローカル変数が使えれば上に述べたように都合が良いだけ
でなく、関数の「再帰呼び出し」というプログラミング形式が可能になります。

 プログラムが複雑になり大規模になってゆくと、グローバル変数とローカル変数とい
う分類だけではなくて、変数にもっと柔軟できめ細かな属性を与える必要があります。
それがスコープ(オブジェクトの有効範囲)、記憶クラス(オブジェクトの寿命)、リ
ンケージ(結合)です。

3.15.2 スコープ

 スコープ( scope)とは、プログラムの中で識別子(名前)にアクセスできる範囲
のことです。スコープにはファイルスコープブロックスコープの2つがあります。
関数、クラスや名前空間(これは後で扱います)の外部で宣言された識別子はファイル
スコープを持ちます。これは、それが宣言された場所からファイルの終わりまでのすべ
ての所で通用します。グローバル変数(オブジェクト)、関数定義および関数プロトタ
イプは、ファイルスコープを持ちます。
 一方、ブロックの内部で宣言された識別子はブロックスコープを持ちます。ブロック
とは括弧{と,}で囲まれた部分です。ブロックスコープの通用範囲は、それが宣言さ
れた場所からブロックの終わりの括弧 } までです。これらは、外からはアクセスでき
ません。関数内部を関数スコープ、クラス内部はクラススコープといいます。
 関数定義において、引数および関数内で宣言されたローカル変数は共に同じ関数スコ
ープを持ちます。そのために、引数の宣言と同じ名前の変数を関数内で宣言することは
できません(名前の衝突)。ブロックがネストされて、ブロックの外側と内側で同じ名
前が使われているときは、外側の名前は内側のブロックが終了するまで隠蔽されます。
次のプログラムは、ネストされた同じ名前の変数のスコープ規則を示しています。
// lst03_35.cpp
// スコープの例
#include <iostream.h>

int i = 1000; // グローバル変数

int main()
{
    cout << "グローバル変数: " << i << endl;

    int i = 100; // ローカル変数

    {                // ブロックの始まり
        int i = 10; // ブロック内のローカル変数
        cout << "ブロック内のローカル変数: " << i << endl; 
    }
    cout << "ローカル変数: " << i << endl;

    return 0;
}
[実行結果]      ソースプログラム ( lst03_35.cpp )
グローバル変数: 1000
ブロック内のローカル変数: 10
ローカル変数: 100
このプログラムでは、グローバル変数、main()関数内のローカル変数およびブロック内
のローカル変数に同じ名前が使われています。グローバル変数はファイルスコープを持
つので、main()関数内で有効ですが、ローカル変数が宣言された場所で隠されます。次
にブロック内では、この外側のローカル変数も隠されてしまいます。ブロックから抜け
ると外側のローカル変数の名前が復活します。一方、グローバル変数は隠されています。

3.15.3 関数プロトタイプ

 関数プロトタイプや関数定義での名前は、ファイルスコープを持ちます。そのため、
関数が宣言または定義された所からファイルの末尾までの間のどの関数からも、その関
数を呼び出すことができます。この様なグローバル関数をmain()関数から呼び出すとき、
次のような2通りのプログラミング・スタイルが可能です。
// スタイル1(予め関数プロトタイプ宣言を行う)

void f(); // 関数プロトタイプ宣言
void g(); // 関数プロトタイプ宣言

int main()
{

    f(); // 関数呼び出し
    g(); // 関数呼び出し

    
    return 0;
}

void f()
{
    // 関数定義の本体
}

void g()
{
    // 関数定義の本体
}
// スタイル2(関数定義を先に行っておく)

void f()
{
    // 関数定義の本体
}

void g()
{
    // 関数定義の本体
}

int main()
{

    f(); // 関数呼び出し
    g(); // 関数呼び出し

    
    return 0;
}
スタイル1で示すように、関数プロトタイプ宣言は関数の名前をファイルスコープの中
に入れる働きをします。それによって、ドライバのmain()関数から呼び出すことができ
るようになります。

3.15.4 記憶クラス

 記憶クラス(storage class)は、識別子がメモリ内に存在する寿命を決定します。
ここでの関心事は、変数(オブジェクト)が保持している値(データ)がいつまで失わ
れずに保たれるかということです。
 記憶クラスには、大きく自動記憶クラス(一時的寿命)と静的記憶クラス(恒久的
寿命)の2つがあります。また、記憶クラスには次の4種類の記憶クラス指定子があ
ります。

  auto(自動)、register(レジスタ)、extern(外部)、static(静的)

これらのキーワードを宣言の先頭に付けることで記憶クラスやリンケージが決まります。
前の2つは自動記憶クラスに関するもので、後ろの2つは静的記憶クラスに関係します。
ただし、重要なのは後ろの2つのキーワードです。

 ローカル変数(オブジェクト)はデフォルトで自動記憶クラスです。この様なローカ
ル変数のことを単に自動変数と呼びます。これは、ブロックに入るときに変数(オブ
ジェクト)のメモリが割り当てられ、ブロックから出るとメモリは解放されます。関数
内の自動変数は、その関数が呼ばれる度に生成・初期化されて、関数が戻るときに消滅
します。自動記憶クラスになれるのは変数(オブジェクト)だけです。


補足auto(自動)と register(レジスタ)  自動記憶クラスの変数を指定するには、autoregisterのどちらかを使 います。しかしこれらは、ほとんど使うことはありません。  autoは自動記憶クラスを指定するキーワードですが、ローカル変数(オブ ジェクト)はデフォルトで自動記憶クラスだからです。  また、キーワードregisterは、ローカル変数の記憶場所をメモリ内ではな く高速なプロセッサ内のレジスタに置くことをコンパイラに示唆するものです。 ただし、十分な個数のレジスタを利用できない場合(これはハードウェアに依 存する)、コンパイラはこれを無視します。現在のコンパイラの最適化機能は、 プログラマがregister宣言しなくても、それを自ら判断してくれます。
 グローバル変数(オブジェクト)と関数は、静的記憶クラスです。これらのデフォル トの記憶クラス指定子はextern(外部)です。グローバル変数(オブジェクト)は、 プログラムの実行開始時に変数のメモリが割り当てられて、その後プログロラムの実行 中ずっと値を保持しています。  静的記憶クラスのもう1つは、記憶クラス指定子staticを付けて宣言されたローカ ル変数(オブジェクト)です。この場合、その宣言文が初めて実行されたとき変数のメ モリが割り当てられて、その後プログロラムの実行中ずっと値を保持します。簡単なク ラスのオブジェクトを使った例を示します。
// lst03_36.cpp
// 記憶クラスの例
#include <iostream.h>

// 簡単なクラス
class X{

public:
    X(int i = 0) : data(i){
        cout << "オブジェクトの生成:data = " << data << endl;
    }
    ~X(){
        cout << "オブジェクトの消滅:data = " << data << endl;
    }

private:
    int data;
};

X a = 3000; // グローバルオブジェクト

int main()
{
    X a = 100;
    static X b = 200; // 静的ローカルオブジェクト

    cout << "\nブロックに入る前:" << endl;
    {
        X a = 10;
        static X b = 20; // 静的ローカルオブジェクト
    }
    cout << "ブロックから出た後:" << endl << endl;

    return 0;
}
[実行結果]      ソースプログラム ( lst03_36.cpp )
オブジェクトの生成:data = 3000
オブジェクトの生成:data = 100
オブジェクトの生成:data = 200

ブロックに入る前:
オブジェクトの生成:data = 10
オブジェクトの生成:data = 20
オブジェクトの消滅:data = 10
ブロックから出た後:

オブジェクトの消滅:data = 100
オブジェクトの消滅:data = 20
オブジェクトの消滅:data = 200
オブジェクトの消滅:data = 3000
 復習を1つ。このクラスXのオブジェクトの宣言文は正しくは、

X a(3000); // グローバルオブジェクト

とすべきですが、「X a = 3000;」としてもコンストラクタを呼び出すことができます。
この様な振る舞いをするコンストラクタを、変換コンストラクタと呼びました。この様
な整数からクラス・オブジェクトへの暗黙の型変換を無効にするには、コンストラクタ
の先頭にexplicit を付加して宣言します。 

class X{

public:
    explicit X(int i = 0) : data(i){
        cout << "オブジェクトの生成:data = " << data << endl;
    }
    ・・・・・・・
};
これを明示的コンストラクタといいました。

 ブロック内で宣言された静的ローカル・オブジェクトは、ブロックを抜けた後も存在
し続けます。注意すべきことは、記憶クラスとスコープ規則は全く別の概念です。ロー
カル変数をstatic宣言しても、スコープはローカルのままで変わりません。ブロック
の外からはアクセスできません。変わるのは、変数の値を保持し続けるということです。

 グローバル変数(オブジェクト)はmain()関数が呼び出される前につくられます。上
の実行結果では最初のコンストラクタの呼び出し「オブジェクトの生成:data = 3000」
がそうです。これをもっとはっきりと見るには、lst03_36.cppのグローバル・オブジェ
クトの宣言文をファイルの最後(つまりmain()関数の後ろ)に移動してコンパイルして
みれば分かります。同じ実行結果が得られるはずです。
 グローバル変数(オブジェクト)や静的ローカル変数(オブジェクト)は、生成され
た後はプログラムが終了するまで存在し続けます。上の結果でオブジェクトのデストラ
クタが呼び出される順番を見てください。ブロックから抜けた後は、次のようになって
います。

 オブジェクトの消滅:data = 100   <=== main()関数内の自動オブジェクト
 オブジェクトの消滅:data = 20    <=== ブロック内静的ローカルオブジェクト
 オブジェクトの消滅:data = 200   <=== main()関数内静的ローカルオブジェクト
 オブジェクトの消滅:data = 3000  <=== グローバルオブジェクト

これらの中で最初に消滅するのはmain()関数内の自動オブジェクトです。これはmain()
のブロックの終わり(スコープの終了)にあたります。残りの静的記憶クラスのオブジ
ェクトは、main()関数が制御を返した後(関数の終了後)に消滅します。また、3つの
静的記憶クラスのオブジェクトのデストラクタが呼ばれる順番は、コンストラクタが呼
ばれた時の逆順になります。

注意】複数のソースファイルからなるプログラムでは、各ファイル 内のグローバル変数(オブジェクト)がどの順番で生成し消滅するか は、処理系に依存します。
 次のプログラムは、呼び出された回数を返すカウンタ関数を、静的ローカル変数を使 って表したものです。
// lst03_37.cpp
// 静的ローカル変数
#include <iostream.h>

// カウンタ関数
int counter()
{
    static int c = 0; // 静的ローカル変数
    return ++c;
}

int main()
{
    for(int i = 0; i < 5; i++){
        cout << "呼び出し回数: " << counter() << endl;
    }

    return 0;
}
[実行結果]      ソースプログラム ( lst03_37.cpp )
呼び出し回数: 1
呼び出し回数: 2
呼び出し回数: 3
呼び出し回数: 4
呼び出し回数: 5
関数counter() 内の静的ローカル変数cは、最初に呼び出されたときに1度だけ値が0
に初期化されて、その後存在し続けます。そして、この関数が呼び出される度に値をイ
ンクリメントして返します。

 復習をもう1つ。このカウンタ関数は、参照型(リファレンス)を返すように定義す
ることで次のように表すこともできます。
// lst03_38.cpp
// 静的ローカル変数
#include <iostream.h>

// カウンタ関数
int& counter()
{
    static int c = 0; // 静的ローカル変数
    return c;
}

int main()
{
    for(int i = 0; i < 5; i++)
        cout << "呼び出し回数: " << ++counter() << endl;

    return 0;
}
ソースプログラム ( lst03_38.cpp )
関数counter() は、静的ローカル変数cの参照を返します。cは関数が戻った後も存在
し続けるので、呼び出し側で「++counter() 」とインクリメントすることでcの値を更
新できます。

 記憶クラス指定子staticにはもう1つ別の側面があります。グローバル変数に対して
static宣言した場合、まったく別の意味を持ちます。これについては、後で述べます。

補足デフォルトの初期化  静的記憶クラスの変数(グローバル変数や静的ローカル変数)が、初期化設 定子を使って宣言されていない場合は、0(ゼロ)で初期化されます。一方、 自動変数は初期化してくれません。プログラマが初期化しないと「ゴミ値」で 初期化されます。

3.15.5 記憶クラスとメモリ

 記憶クラスとはメモリ内の記憶領域の分類を意味します。ここで、色々な変数がメモ
リ内にどのように配置されているかを調べましょう。次のプログラムは、グローバル変
数、ローカル変数、静的ローカル変数および、動的に割り当てた変数(名無しの変数)
に対してアドレス演算子&を使ってそれらのアドレス値を表示します。
// lst03_39.cpp
// 記憶クラスとメモリ
#include <iostream.h>

int global = 1;          // グローバル変数

void f()
{
    int a1_local = 100;         // ローカル変数
    static int s1_local = 200; // 静的ローカル変数
    int *p1 = new int(300);    // 動的変数
    cout << "   ローカル変数: " << &a1_local << endl;
    cout << " 静的ローカル変数: " << &s1_local << endl;
    cout << "   ポインタ変数: " << &p1 << endl;
    cout << "     動的変数: " << p1 << endl;

    delete p1;
}

int main()
{
    cout << "int 型のサイズ: " << sizeof(int) << endl;
    cout << "int*型のサイズ: " << sizeof(int*) << endl << endl;

    cout << "[関数の外]" << endl;
    cout << "  グローバル変数: " << &global << endl;

    cout << endl << "[関数 main]" << endl;

    int a2_local = 10;         // ローカル変数
    static int s2_local = 20; // 静的ローカル変数
    int *p2 = new int(30);    // 動的変数

    cout << "   ローカル変数: " << &a2_local << endl;
    cout << " 静的ローカル変数: " << &s2_local << endl;
    cout << "   ポインタ変数: " << &p2 << endl;
    cout << "     動的変数: " << p2 << endl;

    cout << endl << "[関数 f]" << endl;
    cout << " 関数fのアドレス: " << f << endl;
    f(); // 関数呼び出し

    delete p2;

    return 0;
}
[実行結果]      ソースプログラム ( lst03_39.cpp )
int 型のサイズ: 4
int*型のサイズ: 4

[関数の外]
  グローバル変数: 0041914C

[関数 main]
   ローカル変数: 0065FE00
 静的ローカル変数: 00419154
   ポインタ変数: 0065FDFC
     動的変数: 00673098

[関数 f]
 関数fのアドレス: 00401150
   ローカル変数: 0065FDE4
 静的ローカル変数: 00419150
   ポインタ変数: 0065FDE0
     動的変数: 006730A8
それぞれの変数のアドレスを見ると、アドレスの値はいくつかの階層に分かれて配置さ
れていることが分かります。この結果を、図で表したのが下図です。メモリアドレスの
値は下から上へと大きくなるように表現しています。少し長い図ですが、プログラム内
の変数名の記憶クラスの種類と、その実行結果とを合わせて良く見比べてください。
オブジェクトのメモリマップ

変数はすべて、int型かint型へのポインタ(int*型)で、いずれも4バイトの領域を
占めます。また、関数fのアドレス値は、次のようにして求めることができます。

    cout << " 関数fのアドレス: " << f << endl;

丁度、配列名が配列の先頭アドレスを示す定数ポインタであるのと同じように、関数の
名前はメモリ内にある関数のアドレスを意味するポインタです。

 一番下の方には関数が置かれています。その次に静的記憶クラスの変数が置かれ、自
動変数および動的変数という順に階層化されています。これらの記憶領域をそれぞれ順
に、プログラム領域、静的記憶領域、スタック(自動変数の記憶領域)およびヒープ領
域(または自由記憶領域)と呼びます。
 注目すべきことは、ローカル変数で自動変数はスタックに置かれますが、静的ローカ
ル変数はグローバル変数と同じく静的記憶領域に置かれていることです。

 これら4つの領域の配置順番や割り当てのサイズの取り方はマシンやコンパイラなど
の処理系によって違います。上の例では、関数や静的変数など、恒久的な寿命を持つ固
定されたメモリ領域は、下位メモリに置かれています。それより上部には一時的な自動
変数がスタックに置かれます。スタックは、変数の領域割り当てと解放とがリサイクル
される場所です。残りの上方アドレスには、動的に割り当てたメモリ領域が配置されて
います。このヒープメモリに置かれるのは、標準Cのライブラリ関数 malloc() やC++
のnew演算子を使って割り当てられたメモリ領域です。これらは、プログラマがその寿
命を管理します。関数 malloc()によってつくられたものは標準C言語のライブラリ関
数free()で解放され、new演算子を使って割り当てられたものは、delete演算子によ
って解放されます。

3.15.6 リンケージ

 これまでの話は、1つのプログラムファイルを暗に想定してきました。複数のソース
ファイルからなるプログラムの場合では、ソースファイルの間で識別子がどのように扱
われるのでしょうか。
 リンケージ( linkage)は、識別子がそのソースファイルの中だけでしか通用しな
いのか、他のソースファイルでも通用するのかを決定します。すでに見てきたように、
複数のソースファイルからなるプログラムは、個々のソースファイルがコンパイラによ
って別々にコンパイル(翻訳)されてマシン語コードに変換されます。それから、リン
カによって結合(リンク)されて1つの実行ファイルが作られます。リンケージは、フ
ァイル間で参照される識別子を対応するオブジェクトや関数と正しく結び付ける過程の
ことです。

 リンケージの属性には、外部リンケージ内部リンケージの2つがあります。当然
ながらリンケージに関わるのはファイルスコープを持つもの、つまりグローバル変数(
オブジェクト)や関数です。これらのデフォルトの記憶指定子は、extern(外部)で
す。これは外部リンケージを意味し、外部のファイルからアクセスできます。

 外部のソースファイルから、グローバル変数(オブジェクト)にアクセスするには、
利用する各ファイルの中でその変数(オブジェクト)を宣言しなければなりません。こ
のとき、記憶クラス指定子externを先頭に付けて宣言します。宣言文の一般形は、

extern  型  識別子;

となります。記憶クラス指定子externは、宣言した識別子が「同じファイルの別の場
所か、または別のファイルの中で定義されている」ことを意味します。コンパイラは、
この識別子に対する未解決の参照があることをリンカに知らせ、リンカはその識別子の
アドレスを見つけて未解決の参照を解決します。このとき、リンカが識別子本体の定義
を見つけられないときはリンクエラーとなり、実行ファイルの作成に失敗します。

 関数プロトタイプも外部リンケージを持ちますので、他のソースファイルから利用す
ることができます。関数の場合はそれを利用するファイルでプロトタイプ宣言するだけ
で済みます。externは省略できます。つまり、関数プロトタイプは、宣言した関数名
をファイルスコープの中に入れると同時に、その関数の定義が同じファイルの別の所ま
たは別のファイルにあることを意味します。関数を複数のファイルから利用するには、
その関数を呼び出しているすべてのソースファイルで、その関数をプロトタイプ宣言す
るだけです。そしてそれらのファイルを一緒にコンパイルすれば済みます。通常、この
様な関数プロトタイプはヘッダファイルの中に配置することで、複数のソースファイル
から読み込んで利用できます。


 反対に、識別子を他のファイルから見えなくするには、記憶クラス指定子staticを
指定します。グローバル変数(オブジェクト)や関数をstatic宣言すると、外のファ
イルからアクセスできなくなります。これを内部リンケージといいます。内部リンケー
ジは情報隠蔽の役割をします。そのファイル内でしか使用しないグローバル変数や関数
を、他のファイルから見えないように隠します。これによって、他のファイルからの不
要なアクセス権限を無くし、他で使われている識別子との名前の衝突を避けることがで
きます。

 記憶クラス指定子staticの使い方に注意しましょう。static宣言はそれをローカ
ル変数に使う場合と、グローバル変数に使う場合とでは、まったく違う意味を持ちます。
ローカル変数では記憶クラスを静的にしますが、グローバル変数に対してはスコープを
そのファイル内に限定します。

 以上をまとめた、プログラムを示します。
// lst03_40.cpp
// ソースファイルA
#include <iostream.h>

extern int x; // ファイルBの変数を使う
void g();      // ファイルBの関数を使う
void f();     // ファイルAの関数


int main()
{
    cout << "ファイルBの変数x = " << x << endl;
    f();
    g();

    return 0;
}

void f()
{
    cout << "ファイルAの関数fが呼ばれました。" << endl;
}
ソースプログラム ( lst03_40.cpp )
// lst03_41.cpp
// ソースファイルB
#include <iostream.h>

int x = 100;        // グローバル変数
static void f();   // ファイルAからは見えない
void g();           // ファイルAからは見える

void f()
{
    cout << "ファイルBの関数fが呼ばれました。" << endl;
}

void g()
{
    f();
}
[実行結果]      ソースプログラム ( lst03_41.cpp )
ファイルBの変数x = 100
ファイルAの関数fが呼ばれました。
ファイルBの関数fが呼ばれました。
ドライバプログラムのファイルAは、ファイルBで定義された変数xと関数gを使いま
す。また、2つのファイルには、同型で同名の関数fがあります。変数xと関数gを利
用するために、ファイルAで次のように宣言します。

extern int x; // ファイルBの変数を使う
void g();      // ファイルBの関数を使う

externは「他のファイルを探せ」という意味で、変数では必須ですが、関数プロトタ
イプでは省略できます。また関数名fの衝突を避けるために、ファイルBではstatic
宣言することで、ファイルAに対して名前を隠します。


[演習問題 3.13] 上のプログラムで、記憶クラス指定子 externを外すとどうなるか。 またstaticを外すとどうなるか。利用しているコンパイラを使って確かめよ。エラーを 起こす場合、どのようなエラーメッセージを表示するか。もしエラーを起こさないなら、 予期した実行結果が得られるか確認しなさい。 【ヒント】 GNU g++ や Visual C++ ではエラーとなります。Borland C++      ではエラーになりません(警告を表示します)が、上とは異なる      実行結果になります。xの値は0で初期化されます(つまりリン      クされません)。
 C++ではグローバル変数をconst宣言すると内部リンケージとなります。グローバル const変数を他のファイルから参照できるようにする(外部リンケージ化する)には、 宣言本体に明示的にextern宣言する必要があります。上の例で、プログラムBのxを constにする場合は、 // ソースファイルB extern const int x = 100; // externが必要 とする必要があります。一方ソースファイルAでは // ソースファイルA extern const int x; となります。同じく、型名の宣言に使うキーワード typedefも、内部リンケージを持 ちます。グローバルのconsttypedefがデフォルトで内部リンケージを持つので、 これらをヘッダーファイルの中に入れることができます。複数のソースからヘッダーフ ァイルを読み込んでも名前の衝突(変数の2重定義)は起きません。
[演習問題 3.14] C言語では、staticexternを正しく指定しない場合どうなる か。例えば次のプログラムで、externを外すとどうなるか。また、グローバル変数 xがconstの場合はどうなるか。利用しているコンパイラを使って確かめよ。
/*  lst03_42.c
    ソースファイルA
*/
#include <stdio.h>

extern int x; /* externが必要! */

int main(void)
{
    printf("変数x = %d\n", x);

    return 0;
}
/* lst03_43.c
   ソースファイルB
*/

int x = 100; // グローバル変数

ソースプログラム ( lst03_42.c,  lst03_43.c )

【ヒント】 Cではconst修飾したグローバル変数は、デフォルトで
     外部リンケージを持ちます。つまり、constはリンケージ
     属性を変えません。

 Cや以前のC++では、static宣言で関数やグローバル変数(オブジェクト)に内部 リンケージ属性を持たせました。現在の標準C++ではこれと同じ効果を「無名の名前空 間」を使って実現できます。名前空間については、すぐ後のページで扱います。

| 目次  | 前のページ  | 次のページ  | ページの先頭  |

Copyright(c) 2000 Yamada, K