web-dev-qa-db-ja.com

不完全型へのポインターの逆参照

私はこれについて多くの質問を見てきましたが、特定のコードなしで異なる質問をするつもりです。 簡単にタイプが不完全な原因を特定する方法はありますか?私の場合、私は他の誰かのコードを使用しており、ヘッダーが正しくないことは完全に確信していますが、(コンピューターはこれを人間の眼球よりもはるかに速くて優れているので)コンパイラーに言う方法があります、「ちょっとあなたthinkあなたは34行目にタイプXがありますが、実際にはmissing。」エラー自体は、割り当てたときにのみ表示されますが、あまり役に立ちません。

60
nick

先日、誰かが誤って不完全なタイプを使用して、次のようなものを指定するという質問を見ました

struct a {
    int q; 
}; 
struct A *x; 
x->q = 3;

コンパイラは、Aキーワードにより、structが完全に未定義であるにもかかわらず、struct Aが構造体であることを知っていました。

これはC++でのことで、structのこのような使用法は一般的ではありません(そして、結局のところ、足音につながる可能性があります)。 Cで

typedef struct a {
    ...
} a;

その後、タイプ名としてaを使用し、後でstructを省略できます。これにより、名前を誤って入力した場合やヘッダーを忘れた場合に、不完全な型ではなく、未定義の識別子エラーが後でコンパイラーに表示されます。

46
Potatoswatter

どういう意味ですか、割り当てたときにのみエラーが表示されますか?たとえば、GCCで、割り当てが見えない場合:

int main() {
    struct blah *b = 0;
    *b; // this is line 6
}

incompletetype.c:6: error: dereferencing pointer to incomplete type

エラーis 6行目で、不完全な型を完全な型であるかのように使用しました。それまでは元気でした。

間違いは、タイプを定義するヘッダーを含める必要があるということです。しかし、コンパイラーは、どの行に含めるべきかを推測することはできません。関数の外側の行は、ほとんど問題ありません。また、システム上のすべてのテキストファイルをトロールして、それを定義するヘッダーを探し、それを含めることを提案することもありません。

または(良い点、potatoswatter)、meantを使用して実際に存在するが実際にbを指定した場合、blahが定義された行にエラーがあります。ほとんどの場合、変数bの定義を見つけるのはそれほど難しくないはずです。 IDEは通常それを行うことができますが、コンパイラの警告は気にすることはできないでしょう。ただし、使用しているものの定義が見つからない場合は、かなり悪質なコードです。

10
Steve Jessop

別の考えられる理由は、間接参照です。コードが現在のcファイルに含まれていない構造体を参照している場合、コンパイラーは文句を言います。

a-> b-> c // bが現在のcファイルに含まれていない場合のエラー

10
Steeven Li

私は何が問題なのか正確に理解していません。不完全な型は、「欠落」している型ではありません。非競合型は、宣言済みであるが、定義済みではない型です(構造体型の場合)。非定義宣言を見つけるのは簡単です。欠落している定義を見つけることに関しては、コンパイラはここであなたを助けません。それがそもそもエラーを引き起こしたからです。

Cの不完全な型エラーの主な理由は、型名のタイプミスです。これにより、コンパイラが名前を別の名前に一致させることができません(宣言を定義に一致させる場合など)。しかし、ここでもコンパイラーはあなたを助けることができません。コンパイラはタイプミスについて推測しません。

7
AnT

このエラーは通常、構造体の名前がコード内の構造体の初期化と異なる場合に表示されるため、通常、cは配置した構造体の名前を検索し、元の構造体が見つからない場合は通常表示されます、またはそのポインターを指すポインターをポイントすると、エラーが表示されます。

1
catzilla_waw

解決策

C言語について言えば、次の宣言コードが解決策であることが十分にわかりました。

typedef struct ListNode
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

したがって、一般的な規則として、型定義と構造体の名前の両方に同じ名前を付けます。

typedef struct X
{
    // code for additional types here
    X* prev; // reference to pointer
    X* next; // reference to pointer
} X;

B-問題のあるサンプル

次の宣言を実行すると、gccコンパイラによって次の宣言が両方とも不完全であると見なされます。 ;

removed->next->prev = removed->prev;

また、エラー出力で報告された逆参照コードでも同じエラーが発生します。

>gcc Main.c LinkedList.c -o Main.exe -w
LinkedList.c: In function 'removeFromList':
LinkedList.c:166:18: error: dereferencing pointer to incomplete type 'struct ListNode'
     removed->next->prev = removed->prev;

ヘッダーファイル宣言の両方について、以下にリストします。

typedef struct
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

さらにこれ。

typedef struct ListNodeType
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;
0

プログラム全体の最適化を含む考えられるシナリオ以外では、次のようなコードコードが生成されます。

struct foo *bar;
struct foo *test(struct foo *whatever, int blah)
{
  return blah ? whatever: bar;
}

メンバーstruct fooが何を含んでいてもまったく影響を受けません。 makeユーティリティは通常、構造の完全な定義が現れるコンパイル単位を再コンパイルするため、そのような変更が実際に生成されたコードに影響を与えない場合でも、実際に必要としないコンパイル単位から完全な構造定義を省略するのが一般的ですそれらは省略できますが、一般的にこのような省略は警告に値しません。

コンパイラーは、自動または静的期間を持つ型の宣言オブジェクト、型のメンバーを含む集合体の宣言、または構造体または共用体のメンバーにアクセスするコードの処理方法を知るために、完全な構造体または共用体定義を持っている必要があります。コンパイラーが上記の操作のいずれかを実行するために必要な情報を持っていない場合、コンパイラーはそれについてしゃべるしかありません。

ちなみに、標準ではコンパイラが完全なユニオン定義を表示することを要求できるが、診断は必要としない状況がもう1つあります。2つの構造が共通の初期シーケンスで始まり、両方を含むユニオン型がコンパイラで表示される場合構造タイプの1つのポインターを使用してその共通初期シーケンスのメンバーを検査するコードを処理している場合、そのようなコードが他のタイプの構造の対応するメンバーにアクセスしている可能性があることをコンパイラーが認識する必要があります。完全なユニオン型が表示されているときに標準に準拠しているが、そうでない場合はそうではない場合、どのコンパイラが標準に準拠しているかわからない[-fno-strict-aliasingフラグが使用されない限り、 、どちらの場合でも適合コードを生成します]が、適合コンパイラで正しい動作を保証するような方法でCISルールを使用するコードを記述したい場合は、完全なユニオン型定義が見えるようにする必要があります;そうしないと、コンパイラが静かに偽のコードを生成する可能性があります。

0
supercat