私はこれを間違って行っている可能性があり、これはなぜそれが一方のコンパイラで機能し、他方では機能しないのかについての多くの問題です。
私は大きなCアプリケーションを使用しており、他のヘッダーファイル内からヘッダーファイルを含めないというスタイルに従っています。代わりに、前方宣言を使用します。したがって、私は以下を試みています。
// in A.h
typedef struct A_ A;
typedef struct B_ B;
struct A_ {
double a;
B *b;
};
// in B.h
typedef struct B_ B;
struct B_ {
int c;
};
// in C.h
typedef struct A_ A;
typedef struct B_ B;
void function_do_something(A*, B*);
// in C.c
#include "A.h"
#include "B.h"
#include "C.h"
void function_do_something(A* a, B* b) {
...
}
このパラダイムは、Ubuntu 11.10 gccでコンパイルおよび実行されますが、OpenSUSE gccで「typedefの再定義」というコンパイラエラーが発生します。
私はウブヌトゥで開発を行っているので、このパラダイムが正しくない可能性があることに気づきませんでした。これは明らかに間違っているというだけで、Ubuntuのgccはあまりにもいいです
同じスコープで同じtypedefを再宣言することはC++では合法であると確信しているが、Cでは合法ではないため、これに驚いた。
まず、typedef名にはリンケージがありません。
ISO/IEC 9899:1999 + TC3 6.2.6/6:
次の識別子にはリンケージがありません:オブジェクトまたは関数以外であると宣言された識別子[...]
および6.7/3:
識別子にリンケージがない場合、6.7.2.3で指定されているタグを除いて、同じスコープと同じ名前空間にある識別子の宣言は(宣言子または型指定子で)1つだけでなければなりません。
したがって、各typedef宣言が各翻訳単位のファイルスコープで1回だけ表示されるようにする必要があります。
イディオムの一部が欠落しています。前方宣言は定義から独立しているため、別のヘッダーファイルに含める必要があります。
// a_fwd.h
#ifndef A_FWD_H
#define A_FWD_H
typedef struct A_ A;
#endif
// a.h
#ifndef A_H
#define A_H
#include "a_fwd.h"
struct A_ {
};
#endif
これで、ヘッダーを任意の順序で含めることが常に安全になります。
何かについて2つの定義を持つことは違法です。 typedefは単なる宣言ではなく定義であるため、1つのコンパイラーは冗長性を実現するためにかなり緩やかでした。
Ubuntuコンパイラーは過度にソフトになっています。同じことを2回typedefすることはできません。参照するスタイルでは、インクルードの順序が重要であり、通常、ヘッダーファイルまたはドキュメントでコメントとして言及されます。この場合、次のようになります。
//A.h
typedef struct A A;
struct A {
double a;
B* b;
};
// B.h
typedef struct B B;
struct B {
int c;
};
// C.h
void function_do_something(A*, B*);
// C.c
#include "B.h"
#include "A.h"
#include "C.h"
void function_do_something(A* a, B* b){ ... }
循環依存の場合、これが厄介になることに注意してください。
スタイルの問題と同様に、構造体の後にtypedefを置きます。例:
struct B_ {
int c;
};
typedef struct B_ B;
そのようにあなたは言っています:「ここにB_があり、今私はそれをBと呼びたいです」。逆に、コンパイラで何かをだますことがあります。
複数のヘッダーファイルに同じステートメントを記述して、AとBを再定義しています。 1つの解決策は、AとBのtypedef
をA.hとB.hから削除し、C.hをそのまま使用することです。
他の人がすでに述べたように、Cで型を再定義することはできません。このエラーは、ループされたインクルードまたはその他のロジックの欠陥がある可能性があることを主に示しています。これを回避するには、インクルードファイルをロックすることをお勧めします。
#ifndef __HEADER_H__
#define __HEADER_H__
// Your code goes here
#endif
このようにして、不要なインクルードはこのロックによって省略されます。
あなたの例では、BをAに、AをCに含める必要があります。BをCに含めても効果はなく、コンパイラーを満たします。