extern をローカルで宣言し、変数を登録できるかどうか疑問に思っていました。それが可能である場合、課される制限は何でしょうか?
5 に答える
場合によっては、ローカル変数を extern として宣言できます
C99 N1256 標準案を読んでみましょう。
標準では、「ブロックスコープ」を持つものとして「ローカル変数」を呼び出します。
6.7.1/5「ストレージクラス指定子」には次のように書かれています:
ブロック スコープを持つ関数の識別子の宣言には、extern 以外の明示的なストレージ クラス指定子があってはなりません。
次に、ローカル変数に追加することの意味についてextern
、6.2.2/4「識別子のリンク」は次のように述べています。
その識別子の前の宣言が可視であるスコープ内でストレージ クラス指定子 extern を使用して宣言された識別子の場合、前の宣言が内部リンケージまたは外部リンケージを指定している場合、後の宣言での識別子のリンケージはリンケージと同じです。事前の宣言で指定されています。前の宣言が表示されない場合、または前の宣言でリンケージが指定されていない場合、識別子には外部リンケージがあります。
それらのケースを分解しましょう。
事前申告なし
void f() {
extern int i;
}
以下と同じです:
extern int i;
void f() {}
ただし、宣言は 内でのみ表示されf
ます。
これはi
、以前の宣言が表示されていないためです。外部リンケージi
(グローバル変数と同じリンケージ) も同様です。
前の宣言はリンケージを指定しない
int i;
void f() {
extern int i;
}
以下と同じです:
void f() {
extern int i;
}
int i
パラグラフ6が次のように述べているため、前の宣言はリンケージを指定していないためです。
次の識別子にはリンケージがありません。オブジェクトまたは関数以外のものとして宣言された識別子。関数パラメーターとして宣言された識別子。ストレージ クラス指定子 extern なしで宣言されたオブジェクトのブロック スコープ識別子。
事前宣言は、内部または外部リンケージを指定します
extern int i;
void f() {
extern int i;
}
以下と同じです:
extern int i;
void f() {}
と:
static int i;
void f() {
extern int i;
}
以下と同じです:
static int i;
void f() {}
どちらの場合も、以前に表示されている外部および内部 ( static
) リンケージ宣言がそれぞれあるためです。
ローカル外部を初期化する
無効な C:
void f() {
extern int i = 0;
}
ブロック スコープ宣言には初期化があるためです。
有効な C:
extern int i = 0;
void f() {}
しかし、より短いものと同等であるため、間違いなく悪いスタイルです:
int i = 0;
void f() {}
6.7.8初期化が言うので:
識別子の宣言にブロックスコープがあり、識別子に外部または内部リンケージがある場合、宣言には識別子の初期化子がありません。
- ローカル変数を extern として宣言できますか?
いいえ。ただし、グローバル変数はextern
ローカルで宣言できます。
// file1.c
int Count;
// file2.c
void foo(void) {
extern int Count;
Count++;
}
- レジスタ変数を extern として宣言できますか?
いいえ。変数は と ではない場合がextern
ありregister
ます。
C11 dr 6.7.1 ストレージ クラス指定子
1ストレージ クラス指定子:
typedef
extern
static
_Thread_local
auto
register
制約
2 最大で 1 つのストレージ クラス指定子を宣言の宣言指定子で指定でき_Thread_local
ますstatic
。extern
6.9 C99状態の外部定義:
ストレージクラス指定子autoおよびregisterは、外部宣言の宣言指定子に表示されないものとします。
このフレーズregister variable
は私には明確ではないので、OPが本当に興味を持っているものについて大胆な推測を1つ行い、元の質問をCould local variables be declared with extern specifier?
次のスニペットに示すように言い換えます:
int main() {
extern int x; // Is this OK?
return 0;
}
答えはイエスです。
スコープ (可視性) とストレージは、2 つの独立した接続された概念です。ここでx
は、1 つのローカル変数 (スコープ) があり、このブロック内でのみ表示されます。extern
つまり、これは単なる 1 つの宣言であり、この変数は別の場所で定義されています。明確な参照のためにC標準をお勧めします。
省略されたregister
部分については、OPregister
はregister int x
. register
次に、とextern
を同時に指定することは違法です。
int main() {
extern auto int x; // This is wrong.
return 0;
}
At most, one storage-class specifier may be given in the declaration specifiers in a declaration, except that _Thread_local may appear with static or extern.
auto
対称的な質問は次のとおりです。グローバル変数または外部変数を指定することは有効ですregister
か、これはまさにAlexey Frunzeの答えです。
auto int x; // This is wrong.
int main() {
return 0;
}
グローバル変数は としてのみ定義できますextern
。他の場所で定義されていることをコンパイラ (およびリンカー) に伝えます。
ローカル変数は、スタックまたはレジスタで作成されるため、ローカル スコープにのみ存在します。実行が (もはや) スコープ内にない場合、スタックはアンロールされます (したがって、空き領域が再び利用可能になります) か、レジスタは他の目的に使用され、変数は (もはや) 存在しません。
そのため、ローカル extern を定義することは「奇妙」で不可能です (スタックの使用が原因で)。