12

answer warning: assignment from incompatible pointer type for linklist arrayに答えている間、キーワードでパーシードされた宣言されていない識別子structは、前方宣言された識別子と見なされることに気付きました。

たとえば、以下のプログラムはうまくコンパイルされます。

/* Compile with "gcc -std=c99 -W -Wall -O2 -pedantic %" */
#include <stdio.h>

struct foo 
{
    struct bar *next;  /* Linked list */
};


int main(void) {
    struct bar *a = 0;
    struct baz *b = 0;
    struct foo c = {0};

    printf("bar -> %p\n", (void *)a);
    printf("baz -> %p\n", (void *)b);
    printf("foo -> %p, %zu\n", (void *)&c, sizeof c); /* Remove %zu if compiling with -ansi flag */
    return 0;
}

私の質問:宣言されていないs を前方宣言された不完全な型としてC扱うようにコンパイラを導くルールはどれですか?struct identifierstruct

4

4 に答える 4

10

標準は言う(6.2.5.28

構造体型へのすべてのポインターは、相互に同じ表現とアラインメントの要件を持つ必要があります。

これは、コンパイラが、(まだ) 未定義のものであっても、任意の構造体へのポインターを表す方法を知っていることを意味します。あなたのプログラムはそのような構造へのポインター
のみを扱うので、問題ありません。

于 2015-06-11T09:14:31.100 に答える
7

6.2.5 タイプと 6.7.2.3 タグで説明されています。

struct identifierオブジェクト型です。

6.2.5 タイプ

  1. オブジェクトに格納された値または関数によって返された値の意味は、それにアクセスするために使用される式の型によって決まります。(オブジェクトとして宣言された識別子は、そのような最も単純な式です。型は、識別子の宣言で指定されます。)型は、オブジェクト型 (オブジェクトを記述する型) と関数型 (関数を記述する型) に分割されます。翻訳単位内のさまざまなポイントで、オブジェクト タイプは不完全 (そのタイプのオブジェクトのサイズを決定するための十分な情報がない) または完全 (十分な情報がある) である可能性があります。37)

37) 型は、翻訳単位全体を通して不完全または完全である可能性があります。または、翻訳単位内の異なるポイントで状態が変化する可能性があります。

  1. サイズが不明な配列型は不完全な型です。そのタイプの識別子については、後の宣言でサイズを指定することによって完了します (内部または外部リンケージを使用)。 内容が不明な構造体または共用体型 (6.7.2.3 で説明) は、不完全な型です。そのタイプのすべての宣言について、同じ構造体または共用体タグを、後で同じスコープ内で定義内容とともに宣言することによって、それが完了します。

6.7.2.3 タグ

  1. 同じスコープを持ち、同じタグを使用する構造体、共用体、または列挙型のすべての宣言は、同じ型を宣言します。タグが存在するかどうか、または同じ翻訳単位内に型の他の宣言があるかどうかに関係なく、内容を定義するリストの右中括弧の直後まで型は不完全であり、その後は完全です。

129) 不完全型は、その型のオブジェクトのサイズが必要ない場合にのみ使用できます。たとえば、typedef 名が構造体または共用体の指定子として宣言されている場合、または構造体または共用体を返すポインターまたは関数が宣言されている場合は必要ありません。(6.2.5 の不完全な型を参照してください。) そのような関数が呼び出されるか定義される前に、仕様が完全でなければなりません。

于 2015-06-09T07:51:18.903 に答える
6

2501 から提供された回答と、それに対する「私の場合、前方宣言すらありません」というコメントに加えて、次のとおりです。

struct tag以前に宣言されていない場合、 a の使用は、構造体型の (前方) 宣言としてカウントされます。より正式な言い方をすれば、これは単に型としてカウントされると言うことになりますが、C 標準では「構造体型の前方宣言」について言及されていないため、完全な構造体型と不完全な構造体型 (6.2.5p22)のみが言及されています。

6.7.2 型指定子struct-or-union-specifiertype-specifierであることを示し、6.7.2.1 構造体および共用体指定子 パラグラフ 1は、構造体識別子struct -or-union-specifierであることを示しています。

次のような連結リスト宣言があるとします。

struct node {
    struct node *next;
    int element;
};

この不完全な型の「暗黙の前方宣言」は、この構造が機能するために不可欠です。結局のところ、タイプは終了セミコロンでstruct node のみ完了します。nextただし、ポインターを宣言するには、それを参照する必要があります。

また、struct node(不完全な型の) 宣言は、他の宣言と同様にスコープ外になる可能性があります。これは、たとえば、プロトタイプがある場合に発生します

int function(struct unknown *parameter);

struct unknown、宣言の最後ですぐにスコープ外になります。さらに宣言されたstruct unknowns は、これと同じではありません。これは、 6.2.5p22のテキストで暗示されています。

内容が不明な構造体または共用体型 (6.7.2.3 で説明) は、不完全な型です。そのタイプのすべての宣言について、同じ構造体または共用体タグを、後で同じスコープ内で定義内容とともに宣言することによって完了します。

そのため、gcc は次のように警告します。

foo.c:1:21: warning: 'struct unknown' declared inside parameter list
foo.c:1:21: warning: its scope is only this definition or declaration, which is probably not what you want

これは、その前に追加の前方宣言を置くことで修正できます。これにより、スコープがより早く開始されます (したがって、後で終了します)。

struct unknown;
int function(struct unknown *parameter);
于 2015-06-16T21:52:32.463 に答える
2

不完全な構造体型が使用される最もエレガントなユースケースは次のようなものだと思います:

struct foo 
{
    struct bar *left;
    struct bar *right;
};
struct bar
{
    int something;
    struct foo *next;
};

つまり、a が b を指し、b が a を指す二重再帰です。このようなケースが、この機能が元の C 言語仕様に含まれていた理由である可能性があります。

元の質問は、すべての構造体識別子が自動的に前方宣言されるかどうかです。不完全な struct 定義はすべて自動的に前方宣言と見なされると言ったほうがよいと思います。

編集:ドキュメントに関するコメントに続いて、C 言語の聖書を見てみましょう: Kerninghan&Ritchie - The C Programming Language、セクション「6.5 自己参照構造」には次のように書かれています。

場合によっては、自己参照構造のバリエーション、つまり相互に参照する 2 つの構造が必要になることがあります。これを処理する方法は次のとおりです。

struct t {
    ...
    struct s *p;   /* p points to an s */
};
struct s {
    ...
    struct t *q;   /* q points to a t */
};

別の方法で実装できることには同意しますが、これは C 言語の作成者からの良い動機であると考えており、これを実装するのがエレガントな方法であることに同意します。

于 2015-06-17T09:14:08.007 に答える