C言語　記憶域期間、リンケージ、記憶域クラス指定子

記憶領域やリンケージについて、記憶域クラス指定子と共にまとめました。

以下のURLの内容を自分なりにまとめたものになります。
[記憶域クラス指定子 - cppreference.com](https://ja.cppreference.com/w/c/language/storage_duration)


# 記憶域期間

C言語のオブジェクトは、記憶領域期間と飛ばれる性質を持ちます。オブジェクトの生存期間を制限するもので、3種類あります。

今回はVLA, _Thread_localの説明は除きます。

## 自動記憶域期間

この記憶域は、そのオブジェクトが宣言されたブロックに入ったときに確保され、何らかの方法 (goto、 return、終わりに到達) によって終了したときに解放されます。

ブロックに再帰的に入った場合は、再帰の各段について新たな確保が行われます。すべての関数の仮引数および非 static なブロックスコープのオブジェクトはこの記憶域期間を持ちます。

=> ローカル変数や、仮引数。

## 静的記憶域期間

この記憶域はプログラムの実行全体であり、そのオブジェクトに格納される値はmain関数の前に一度だけ初期化されます。

- static 宣言されたすべてのオブジェクト
- 内部または外部いずれかのリンケージを持つすべてのオブジェクト

```c
char test[] = "aiueo";
```

## 確保された記憶域期間

この記憶域は動的メモリ確保関数を用いて要求に応じて確保および解放されます。

=> malloc等で確保される領域。

# 3つのリンケージ

リンケージは識別子 (変数または関数) が他のスコープで参照されることができるかどうかを表します。

同じ識別子を持つ変数または関数がいくつかのスコープで宣言されたけれども、そのすべてから参照することができない場合、その変数のいくつかのインスタンスが生成されます。

以下のリンケージが認識されます。

## リンケージなし

識別子はその存在するスコープからのみ参照できます。

すべての関数の仮引数およびすべての extern でないブロックスコープの変数 (static 宣言されたものを含む) はこのリンケージを持ちます。

## 内部リンケージ

識別子は現在の翻訳単位内のすべてのスコープから参照できます。

すべての static なファイルスコープの識別子 (関数と変数の両方) はこのリンケージを持ちます。

=> ブロック内で保持され、再度呼び出しされたときに、値が生き続けるオブジェクト。
=> 関数内で宣言されたstaticな変数。

## 外部リンケージ

プログラム全体のあらゆる翻訳単位から参照できます。

すべての static でない関数、すべての extern 変数 (すでに static 宣言されていたものを除く)、およびすべてのファイルスコープの static でない変数はこのリンケージを持ちます。

=> グローバル変数。

## 例

```c
// flib.h
#ifndef FLIB_H
#define FLIB_H

void f(void);              // 外部リンケージを持つ関数の宣言
extern int state;          // 外部リンケージを持つ変数の宣言
static const int size = 5; // 内部リンケージを持つ読み込み専用の変数の定義
enum { MAX = 10 };         // 定数の定義
inline int sum (int a, int b) { return a+b; } // インライン関数の定義
#endif // FLIB_H
```

```c
// flib.c
#include "flib.h"

static void local_f(int s) {}  // 内部リンケージを持つ定義 (このファイルでのみ使用されます)
static int local_state;        // 内部リンケージを持つ定義 (このファイルでのみ使用されます)

int state;                     // 外部リンケージを持つ定義 (main.c で使用されます)
void f(void) {local_f(state);} // 外部リンケージを持つ定義 (main.c で使用されます)
```

# 記憶域クラス指定子

指定子 | 記憶域期間 | リンケージ | 詳細
--- | --- | --- | ---
auto | 自動記憶域期間 | なし |
register | 自動記憶域期間 | なし | この変数のアドレスを取ることはできません
static | 静的記憶域期間 | 内部リンケージ | リンケージはブロックスコープの場合を除く
extern | 静的記憶域期間 | 外部リンケージ | リンケージはすでに内部リンケージとして宣言されている場合を除く

## 説明

記憶域クラス指定子は宣言内に現れます。

多くとも1つの指定子を指定することができます。
記憶域クラス指定子は、その宣言対象の名前に対する独立した2つの性質、記憶域期間とリンケージを決定します。

### auto 指定子

ブロックスコープ(カッコで括られれている領域)で宣言されるオブジェクト (関数の仮引数リストを除く) に対してのみ使用できます。

これは自動記憶域期間とリンケージなしを表します (これはこれらの種類の宣言に対するデフォルトです)。

=> 普通、省略されている。関数内で指定子なしで制限される変数はこれ。

### register 指定子

ブロックスコープで宣言されるオブジェクト (関数の仮引数リストを含む) に対してのみ使用できます。

これは自動記憶域期間とリンケージなしを表します (これはこれらの種類の宣言に対するデフォルトです) が、さらに、可能であればこの変数の値を CPU のレジスタに格納するよう最適化のヒントを与えます。

この最適化が行われるか否かに関わらず、 register 宣言された変数はアドレス取得演算子の引数として使用することができません。

register 配列はポインタに変換できません。

### static 指定子

静的記憶域期間と内部リンケージ (ブロックスコープの場合を除く) を指定します。

ファイルスコープの関数およびファイルスコープとブロックスコープ両方の変数で使用することができますが、関数の仮引数リストでは使用できません。

### extern 指定子

静的記憶域期間と外部リンケージを指定します。

ファイルスコープとブロックスコープ両方の関数およびオブジェクト (関数の仮引数リストを除く) で使用できます。 すでに内部リンケージで宣言された識別子の再宣言で extern が現れた場合は、そのリンケージは内部のままです。 そうでなければ (前の宣言が外部またはリンケージなしであるか、スコープ内にない場合)、リンケージは外部です。

記憶域クラス指定子が提供されない場合は、デフォルトは以下の通りです。

- すべての関数に対して ー extern
- ファイルスコープのオブジェクトに対して ー extern
- ブロックスコープのオブジェクトに対して ー auto

記憶域クラス指定子付きで宣言されたあらゆる構造体または共用体について、その記憶域期間がそのメンバに再帰的に適用されます (しかしリンケージは適用されません)。

- ブロックスコープの関数宣言は、 extern を使用でき、または何も使用しないこともできます。
- ファイルスコープで宣言された関数は、 extern または static を使用できます。

関数の仮引数は、 register 以外、何の記憶域クラス指定子も使用することができません。 関数の配列型の仮引数では static に特別な意味があることに注意してください。