私はCプログラミングの初心者であり、struct
型宣言とtypedef
struct宣言の違いを知っています。私はstruct
を次のように定義すると言う答えを知りました:
typedef struct {
some members;
} struct_name;
次に、匿名の構造体にエイリアスを提供するようなものになります(タグ名がないため)。そのため、前方宣言には使用できません。 前方宣言の意味がわかりません。
また、次のコードについても知りたいと思いました。
typedef struct NAME {
some members;
} struct_alias;
NAME
とstruct_alias
に違いはありますか?または、struct_alias
がNAME構造体のエイリアスであるため、両方とも等しいですか?
さらに、次のようなstruct NAME
型の変数を宣言できますか?
struct_alias variable1;
および/またはような:
struct NAME variable2;
またはのような:
NAME variable3;
struct
forward宣言は、ループ構造の宣言が必要な場合に役立ちます。例:
struct a {
struct b * b_pointer;
int c;
};
struct b {
struct a * a_pointer;
void * d;
};
struct a
が宣言されたとき、struct b
の仕様はまだわかりませんが、前方参照できます。
匿名構造体をtypedefすると、コンパイラはtypedefの前にその名前を使用することを許可しません。
これは違法です:
struct a {
b * b_pointer;
int c;
};
typedef struct {
struct a * a_pointer;
void * d;
} b;
// struct b was never declared or defined
ただしこれは有効です:
struct a {
struct b * b_pointer;
int c;
};
typedef struct b {
struct a * a_pointer;
void * d;
} b;
// struct b is defined and has an alias type called b
これもそうです:
typedef struct b b;
// the type b referes to a yet undefined type struct b
struct a {
b * struct_b_pointer;
int c;
};
struct b {
struct a * a_pointer;
void * d;
};
そしてこれ(Cのみ、C++では違法):
typedef int b;
struct a {
struct b * struct_b_pointer;
b b_integer_type;
int c;
};
struct b {
struct a * a_pointer;
void * d;
};
// struct b and b are two different types all together. Note: this is not allowed in C++
前方宣言は、定義を行うことができない時点でコンパイラに対して行うことを定義する約束です。コンパイラーはWordを使用して、他の方法では解釈できない他の宣言を解釈できます。
一般的な例は、リンクリストのノードになるように設計されたstruct
です。ノードへのポインターをstruct
に入れる必要がありますが、コンパイラーは、前方宣言またはタグ:
// Forward declaration
struct element;
typedef struct {
int value;
// Use of the forward declaration
struct element *next;
} element; // Complete definition
したがって、前方宣言には使用できません
著者のポイントは、あなたのstruct
タグを与えることは前方宣言と同等だということだと思います:
typedef struct element {
int value;
// No need for a forward declaration here
struct element *next;
} element;
前方宣言は、通常、定義が利用できないときに宣言された型を参照できるようにするために、実際の定義に先行する宣言です。もちろん、すべてが宣言未定義構造で行われるとは限りませんが、特定のコンテキストではそれを使用することができます。そのようなタイプはincompleteと呼ばれ、その使用には多くの制限があります。例えば:
struct X; // forward declaration
void f(struct X*) { } // usage of the declared, undefined structure
// void f(struct X) { } // ILLEGAL
// struct X x; // ILLEGAL
// int n =sizeof(struct X); // ILLEGAL
// later, or somewhere else altogether
struct X { /* ... */ };
これは便利です。通常は定義が非常に大きいため、循環依存関係を解消したり、コンパイル時間を短縮したりするため、解析にはより多くのリソースが必要です。
あなたの例では、struct NAME
およびstruct_alias
は実際に同等です。
struct_alias variable1;
struct NAME variable2;
は正しい;
NAME variable3;
cでは、struct
キーワードは必須ではありません。
struct_alias
とstruct NAME
は同じです、struct_alias
はstruct NAME
のエイリアスです
これらは両方とも同じで許可されています
struct_alias variable1;
struct NAME variable1;
これは違法です
NAME variable3;
前方宣言 に関するこの記事を参照してください
他の人が以前に述べたように、C/C++の前方宣言は、実際の定義が利用できない何かの宣言です。コンパイラに「データ型ABCがあります」と伝える宣言。
これをいくつかのキー/値ストア_my_dict.h
_のヘッダーであるふりをしましょう:
_...
struct my_dict_t;
struct my_dict_t* create();
char* get_value(const struct my_dict_t* dict, const char* name);
char* insert(struct my_dict_t* dict, const char* name, char* value);
void destroy(struct my_dict_t* dict);
...
_
_my_dict_t
_については何も知りませんが、実際には、ストアを使用するために知る必要はありません:
_#include "my_dict.h"
...
struct my_dict_t* dict = create();
if(0 != insert(dict, "AnEntry", strdup("AValue"))) {
...
}
...
_
その理由は次のとおりです。データ構造へのポインターのみを使用しています。
ポインターは単なる数字であり、それらに対処するために、それらが何を指しているのかを知る必要はありません。
これは、実際にそれらにアクセスしようとした場合にのみ重要です
_struct my_dict_t* dict = create();
printf("%s\n", dict->value); /* Impossible if only a forward decl is available */
_
したがって、関数を実装するには、_my_struct_t
_の実際の定義が必要です。ソースファイル_my_dict.c
_で次のようにすることができます:
_#include "my_dict.h"
struct my_dict_t {
char* value;
const char* name;
struct my_dict_t* next;
}
struct my_dict_t* create() {
return calloc(1, sizeof(struct my_dict_t));
}
_
これは、次のようないくつかの状況で便利です。
したがって、残っている問題は、上記の関数を使用するときに前方宣言をまったく省略できないのはなぜですか?結局、コンパイラーはすべてのdict
がポインターであることを知るだけで十分です。
ただし、コンパイラは型チェックを実行します。次のようなことをしないことを確認する必要があります
_...
int i = 12;
char* value = get_value(&i, "MyName");
...
_
_my_dict_t
_がどのように見えるかを知る必要はありませんが、_&i
_はget_value()
が期待するポインターのタイプではないことを知る必要があります。