C の知識:記憶クラスとリンケージ
記憶クラス指定
変数は基本的に宣言される場所によってスコープが決まり、それに伴って仕舞う場所も決まります。即ちグローバル変数はプログラム全体を通して固定サイズの領域を占めるのでデータセグメントへ、ローカル変数は関数毎に変わるのでスタックへと格納されます。
種類 | 宣言する場所 | スコープ | 生存期間 |
---|---|---|---|
グローバル変数 | 関数の外 | ファイル全体 | 実行中ずっと |
ローカル変数 | ブロックの中(関数も) | そのブロックの中 | そのブロックの実行中 |
生存期間が終われば、その変数(の占めていた領域)はメモリの海に還されます。
しかしstaticキーワードをローカル変数の宣言に付けると、(スコープはそのままで)生存期間がプログラムの実行中ずっとになります。
例えば
int countup(void) | |
{ | |
static int n = 0; | |
return ++n; | |
} |
を何度も呼び出すと、処理がスコープの外に出てもnはずっとメモリ上の同じ領域を占有し続けるので、その返り値は1 2 3 ...となります。(n = 0の初期化は最初の一回だけ行われます。)
またローカル変数の宣言にregisterを付けると、コンパイラに「メモリでなくレジスタに記憶してほしい」という意思を伝える事ができます。(コンパイラが従ってくれるとは限らないのですが、いずれにせよ&でアドレスを求めたりは出来なくなります。)
保管場所の違いはありますが、スコープと生存期間に関しては関数も基本的にグローバル変数と同じです。
リンケージ
「C の知識:分割コンパイル」で別のファイルで定義した関数を呼び出す、という事をしましたが、ここで注意して頂きたいのが「main.c と stat.c でのmean関数のスコープは独立している」という事です。
つまり、#include "stat.h"を展開すると stat.c は
#include <stdlib.h> | |
#include <math.h> | |
double mean(int n, double *data); | |
double mean(int n, double *data) | |
{ | |
... |
となっていて、main.c は
#include <stdio.h> | |
double mean(int n, double *data); | |
int main(void) | |
{ |
となっています。
関数のスコープは宣言されたファイル全体なので、これらのmeanは別々のスコープを持ち、特に main.c から stat.c の中のmean関数は見えないのではないかという事です。
これが上手く動くのはリンケージ(linkage)の作用によるものです。関数や変数のリンケージとは、「同じ名前のものとどこまで同一視するか?」を決める属性です。
外部リンケージ(external linkage)の場合、ファイル(スコープ)の外であっても名前が同じならば同じものと看做され、その実体はプログラム全体で唯一になります。関数やグローバル変数はデフォルトで外部リンケージを持ちます。
関数やグローバル変数の宣言にstaticを付けると内部リンケージ(internal linkage)になります。この場合、ファイルの外で同じ名前のものが宣言されても同一視されません。
またローカル変数にリンケージはありません。