Cプログラミング
 Computer >> コンピューター >  >> プログラミング >> Cプログラミング

C言語における変数のバインディング(束縛)とは?記憶クラスとスコープの基本を解説

C言語において、記憶クラス(storage class)は変数の「スコープ(有効範囲)」「生存期間」「バインディング(束縛)」を決定する重要な要素です。変数を完全に定義するためには、そのだけでなく、記憶クラスも併せて指定する必要があります。

変数名とは、コンピュータのメモリ上の物理的な位置を識別するものであり、そこには変数の値を格納するためのビット列が割り当てられています。

記憶クラスから分かること

記憶クラスを指定すると、以下の4つの情報が決まります。

  • 変数がどこに格納されるか(メモリ上か、CPUレジスタか)
  • 明示的に初期化しなかった場合の初期値は何になるか
  • 変数のスコープ(どこからアクセス可能か)
  • 変数の寿命(生存期間)

バインディング(束縛)とは

バインディングとは、識別子が使用されている箇所(適用出現)に対して、対応する宣言・定義(束縛出現)を見つけ出す処理のことです。

バインディングを正しく理解するためには、次の2つのポイントを押さえておく必要があります。

1. 変数のスコープを把握する

プログラムのブロック構造がどのようになっているのか、そして対象の識別子がどのブロックに属しているのかを理解しましょう。

2. 同じ識別子名を再利用した場合の挙動

C言語では、同一のスコープ内で同じ識別子名を使用することは禁止されています。コンパイルエラーとなるため注意が必要です。一方で、スコープが異なれば同じ名前を使うことは可能です。

NG例:同じスコープ内での重複定義

double f, y;
int f( )   // エラー:fはすでにdouble型変数として宣言済み
{
   ---
}
double y;  // エラー:yはすでに宣言済み

このように、同じスコープ内で同じ名前を再度宣言するとエラーになります。

OK例:異なるスコープでの同名使用

double y;
int f( ){
    double f; // 合法:関数内スコープのため問題なし
    int y;    // 合法:グローバルのyとは別物として扱われる
}

関数の中であれば、外部で使われている名前と同じ識別子を宣言しても問題ありません。

サンプルプログラム

続いて、変数のバインディングの動作を実際に確認できるC言語プログラムを紹介します。

#include<stdio.h>
int i = 33;
main() {
    extern int i; {
        int i = 22; {
            const volatile unsigned i = 11;
            printf("i=%d\n", i);
        }
        printf("i=%d", i);
    }
}

実行結果

このプログラムをコンパイルして実行すると、次のような出力が得られます。

i=11
i=22

解説

この結果から、以下のことが分かります。

  • 最も内側のブロックで宣言された i = 11 が最初に出力されます。内側のスコープほど優先的に参照されるためです。
  • 内側のブロックを抜けると、一つ外側のブロックで宣言された i = 22 が参照されます。
  • グローバル変数の i = 33 は出力されません。これは、より内側のスコープで同じ名前 i が宣言され、それが優先されたためです。

このように、C言語では最も内側のスコープにある宣言が最優先で結び付けられるというのが、バインディングの基本的なルールです。スコープと記憶クラスの関係をしっかり理解しておくことで、意図しない変数参照によるバグを防ぐことができます。

  1. 構造体の概念で理解するC言語のビットフィールド|定義方法と範囲の計算を徹底解説

    ビットフィールドとはビットフィールドとは、変数が占めるメモリのサイズをビット単位で指定できるC言語の機能です。通常は構造体(struct)の中で定義されます。ビットフィールドの基本:1バイト=8ビット記述例struct info { int x : 2; };この例では、メンバxは2ビットを占有します。ビットフィールド使用時の注意点ビットフィールドの範囲外の値を代入することはできません(その場合の動作は保証されません)。sizeof演算子やアドレス演算子(&)をビットフィールドに適用できないため、scanf関数で値を入力することもできません。ビットフィールドに指定できるデータ型

  2. C言語におけるユニオンとポインタの使い方を徹底解説

    ユニオン(共用体)とは、異なるデータ型を持つ複数の変数が、同一のメモリ領域を共有するための仕組みです。構造体が各メンバーごとに独立したメモリを割り当てるのに対し、ユニオンではすべてのメンバーが同じアドレスを参照するという点が大きな特徴です。 ユニオンの構文 ユニオンを定義するときの基本的な書式は次の通りです。 union タグ名{ データ型 メンバー1; データ型 メンバー2; ---- ---- データ型 メンバーn; }; 実際の記述例を見てみましょう。 union sample{ int a; float b; char