2

次のCプログラムがあります:

#include<stdio.h>

static void p(void);
static int c;

int main(int argc,char **argv) {
    p();
    printf("%d",c);
    return 0;
}


void p(void) {
    printf("From the Function P\n");
}

int c=232;

そして、コンパイラ gcc の出力エラーは次のとおりです。 エラー: 'c' の非静的宣言は、静的宣言に従います

そして、私がC標準ISO/IEC 9899:TC2を見たとき:


6.2.2 識別子のリンケージ

1 異なるスコープまたは同じスコープで 2 回以上宣言された識別子は、リンケージと呼ばれるプロセスによって同じオブジェクトまたは関数を参照することができます 21)。

2 プログラム全体を構成する翻訳単位とライブラリのセットでは、外部リンケージを持つ特定の識別子の各宣言は、同じオブジェクトまたは関数を示します。1 つの翻訳単位内で、内部リンケージを持つ識別子の各宣言は、同じオブジェクトまたは関数を表します。リンクのない識別子の各宣言は、一意のエンティティを示します。

3 オブジェクトまたは関数のファイル スコープ識別子の宣言にストレージ クラス指定子 static が含まれている場合、識別子は内部リンケージを持ちます。

4 その識別子の前の宣言が可視であるスコープ内で ストレージクラス指定子 externで宣言された識別子の場合、23) 前の宣言が内部リンケージまたは外部リンケージを指定する場合、後の宣言での識別子のリンケージは同じです。前の宣言で指定されたリンケージとして前の宣言が表示されない場合、または前の宣言でリンケージが指定されていない場合、識別子には外部リンケージがあります。

5関数の識別子の宣言にストレージ クラス指定子がない場合、そのリンケージは、ストレージ クラス指定子 extern で宣言されているかのように正確に決定されますオブジェクトの識別子の宣言にファイル スコープがあり、ストレージ クラス指定子がない場合、そのリンケージは externalです。

6 次の識別子にはリンケージがありません。オブジェクトまたは関数以外のものとして宣言された識別子。関数パラメーターとして宣言された識別子。ストレージ クラス指定子 extern なしで宣言されたオブジェクトのブロック スコープ識別子。

7 翻訳単位内で、同じ識別子が内部リンケージと外部リンケージの両方に現れる場合、動作は未定義です。

21) 異なる識別子間のリンクはありません。22) 関数宣言は、ファイル スコープにある場合にのみ、ストレージ クラス指定子 static を含むことができます。6.7.1 を参照。


Q.1 ルール 4 と 5 がわからないのですが?違いは何ですかリンケージは、ストレージクラス指定子 extern で宣言されているかのように正確に決定されます。そしてリンケージは外部です


Q2. なぜこのエラーが発生するのか、ルール 5 から c に static decl があると推測できるからです。その後に extern decl が続きます。最後のdecl.shudも静的です。誰かが最初からすべてのルールを説明するのに苦労している場合は感謝します..または、すべてのルールを明確に説明するリンクを提案してください.*

注:この質問を適切に編集するための提案については、重複する可能性があるため、このサイトに投稿された同様の質問の回答を事前に理解できなかったため、この質問をしました。

4

4 に答える 4

1

static int c;c はファイルスコープと内部リンケージを持っていることを意味しますが、ストレージクラス指定子なしでstatic後でc再度定義するint cと、デフォルトで外部リンケージを与えようとするため (ルール 5))、同じファイルスコープで以前に宣言されたのとは反対です。

于 2013-08-23T07:07:57.510 に答える
1

外部リンケージを持つ識別子は、実行可能ファイルにリンクされているさまざまな翻訳単位で見ることができます。外部リンケージを持たない識別子は、それを定義した翻訳単位でのみ (おそらく特定のスコープでのみ) 見ることができます。C.99 §5.1.1.1 から:

プログラムの個々の翻訳単位は、(たとえば) 外部リンケージを持つ識別子を持つ関数の呼び出し、外部リンケージを持つ識別子を持つオブジェクトの操作、またはデータ ファイルの操作によって通信します。翻訳単位を個別に翻訳し、後でリンクして実行可能プログラムを生成することができます。

C.99 §6.2.2 ¶4 の実例として、以下を検討してください。

static int c;
void foo () { assert(c == 1); }
int main () {
    extern int c;
    c = 1;
    foo();
    return 0;
}

アサーション条件は true と評価されます。これは、C.99 §6.2.2 ¶4 が、スコープ内に 1 つある場合、 in が以前に宣言されたものと同じリンケージをとるcと述べているためです。この場合は. の宣言がない場合、inは外部リンケージを持つことになります。extern int ccstatic int ccextern int cmain()

C.99 §6.2.2 ¶5 では、最初の文は関数名にのみ適用されます。関数宣言にストレージクラスが含まれていない場合、関数名には外部リンケージがあると言っています。

C.99 §6.2.2 ¶5 の 2 番目の文は、オブジェクトがストレージ クラスなしでファイル スコープで宣言されている場合、そのオブジェクトの名前には外部リンケージがあると述べています。したがって、スニペットで:

static int c;
/*...*/
int c = 5;

c最初に内部リンケージを持つと宣言され、次に外部リンケージを持つと宣言されているため、競合があります。

于 2013-08-23T07:34:20.100 に答える