C11 仕様をまだ誰も引用していないことに驚いています。長い引用で申し訳ありませんが、関連性があると思います。
7.5 エラー
ヘッダーはいくつかのマクロを定義しています...
...と
errno
これは、型とスレッド ローカル ストレージ期間を持つ変更可能な lvalue(201) に展開されint
、その値はいくつかのライブラリ関数によって正のエラー番号に設定されます。実際のオブジェクトにアクセスするためにマクロ定義が抑制されている場合、またはプログラムが という名前の識別子を定義しているerrno
場合、動作は未定義です。
初期スレッドの の値はerrno
、プログラムの起動時にはゼロです (他のスレッドの の初期値はerrno
不定値です) が、ライブラリ関数によってゼロに設定されることはありません。(202) errno の値は、エラーの有無にかかわらず、ライブラリ関数呼び出し。ただし、
errno
この国際規格の関数の説明で使用が文書化されていない場合に限ります。
(201) マクロerrno
は、オブジェクトの識別子である必要はありません。関数呼び出しの結果、変更可能な左辺値に展開される場合があります (たとえば、*errno()
)。
(202) したがって、エラー チェックに を使用するプログラムはerrno
、ライブラリ関数呼び出しの前にゼロに設定し、その後のライブラリ関数呼び出しの前に検査する必要があります。もちろん、ライブラリ関数は、の値が戻る直前に の値がまだゼロであるerrno
場合に元の値が復元される限り、エントリで の値を保存してゼロに設定できます。errno
「スレッドローカル」register
はアウトを意味します。タイプint
は、ビットフィールドがアウトであることを意味します(IMO)。だから&errno
私には合法に見えます。
「それ」や「値」などの言葉が一貫して使用されていることは、標準の作成者が一定ではないことを考えていなかったことを示唆しています。&errno
特定のスレッド内で一定ではない実装を想像できると思います&errno
が、脚注が言うように使用するには(ゼロに設定し、ライブラリ関数を呼び出した後にチェックします)、意図的に敵対的である必要があり、おそらく特殊化が必要になるでしょうコンパイラのサポートは敵対的です。
要するに、仕様が non-constant を許可している場合、&errno
それは意図的なものではないと思います。
[アップデート]
R. はコメントで素晴らしい質問をします。考えてみると、彼の質問と元の質問に対する正しい答えを知っていると思います。親愛なる読者の皆さん、私があなたを納得させることができるかどうか見てみましょう。
R. は、GCC がトップ レベルで次のようなことを許可していると指摘しています。
register int errno asm ("r37"); // line R
errno
これは、 register に保持されているグローバル値として宣言されますr37
。明らかに、それはスレッドローカルの変更可能な左辺値になります。では、適合する C 実装errno
はこのように宣言できますか?
答えはノーです。あなたや私が「宣言」という言葉を使うとき、私たちは通常、口語的で直感的な概念を念頭に置いています. しかし、標準は口語的または直感的に話すものではありません。正確に話し、明確に定義された用語のみを使用することを目的としています。「宣言」の場合、規格自体が用語を定義します。用語を使用するときは、独自の定義を使用しています。
仕様を読むことで、「宣言」とは何か、そうでないものを正確に知ることができます。別の言い方をすれば、標準は言語「C」を記述しています。「C以外の言語」については説明していません。標準に関する限り、「拡張機能付きの C」は「C 以外の言語」にすぎません。
したがって、標準の観点からすると、行 Rはまったく宣言ではありません。解析すらしません!次のように読むこともできます。
long long long __Foo_e!r!r!n!o()blurfl??/**
仕様に関する限り、これは行 R と同じくらい「宣言」です。つまり、まったくありません。
したがって、C11 仕様に記載されているセクション 6.5.3.2 では、次のようになります。
単項演算子のオペランドは&
、関数指定子、単項演算子[]
または単項演算子の結果、または*
ビットフィールドではなく、レジスタ ストレージ クラス指定子で宣言されていないオブジェクトを指定する左辺値のいずれかでなければなりません。
...それは、Line R のようなものを参照しない、非常に正確な何かを意味します。
ここで、参照先のint
オブジェクトの宣言を考えてみましょうerrno
。(注: errno
nameの宣言を意味しているわけではありません。もちろんerrno
、たとえばマクロの場合、そのような宣言は存在しない可能性があるためです。つまり、基になるint
オブジェクトの宣言を意味します。)
上記の言語は、ビットフィールドを指定するか、「宣言された」オブジェクトを指定しない限り、左辺値のアドレスを取得できることを示していますregister
。そして、基礎となるerrno
オブジェクトの仕様は、それがint
スレッドローカル期間を持つ変更可能な左辺値であると述べています。
errno
さて、仕様では、基礎となるオブジェクトを宣言する必要があるとはまったく述べていないのは事実です。たぶん、実装定義のコンパイラマジックを介して表示されるだけです。ただし、仕様で「レジスタ ストレージ クラス指定子を使用して宣言されている」と記載されている場合、独自の用語が使用されています。
そのため、基になるオブジェクトが標準的な意味で「宣言」されているかのどちらかであり、その場合、両方とスレッドローカルerrno
であることはできません。register
またはまったく宣言されていない場合は、宣言されていませんregister
。いずれにせよ、これは左辺値であるため、そのアドレスを取得できます。
(ビットフィールドでない限り、ビットフィールドは type のオブジェクトではないことに同意すると思いますint
。)