C での不完全型エラーへのポインターの逆参照
変数内に格納された値のアドレスを格納するためにポインタが使用されることが知られています。 このポインター変数は、変数を参照してそのアドレスを取得したり、逆参照してその値にアクセスしたりできます。
ただし、このポインターを表示または編集する必要がある場合は、逆参照する必要があります。 宣言されているが定義されていない型を介してこの逆参照が行われると、不完全な型へのポインターを逆参照する
というエラーがスローされます。
C での不完全な型へのポインターの逆参照
エラーの原因
このエラーは、ポインターが構造体に逆参照されていることをコンパイラーが検出したときに発生します。 コンパイラが構造体に向かって移動すると、それが不完全である、つまり適切に定義されていないことがわかります。
ポインターを参照するために未定義の構造体が呼び出されると、コンパイラはさまざまなエラーをスローします。 発生するエラーの種類は、使用するコンパイラによって異なります。
GCC コンパイラでの 不完全型への逆参照ポインタ
エラー
たとえば、未定義の構造体は次のようになります。
struct circle {
int length;
};
int main() {
struct round *x = 0;
*x;
}
上記の C プログラムでは、構造体 circle
が構築されていることがわかりますが、main
関数内で呼び出された構造体には round
という名前が付いています。
main
関数内では、オブジェクト ポインター *x
が構造体 round
から作成されます。 最後に、ポインター オブジェクトが呼び出されます。
ここでは、不完全な構造体が完全な構造体であるかのように使用されるため、コンパイラはエラーをスローします。
1035906452/source.c: In function 'main':
1035906452/source.c:6:5: error: dereferencing pointer to incomplete type 'struct round'
*x;
^~
コンパイラが不完全な型へのポインターを逆参照する
というエラーをスローすることがわかります。
通常、C は入れられた構造体の名前を見つけます。 元の構造体が見つからない場合、通常これが表示されます。 また、そのポインターを指しているポインターをポイントした場合にも表示されます。
このエラーは通常、構造体の名前がコード内の構造体の初期化と異なる場合に表示されます。
構造体は、コンパイラが読み取れないエイリアスを指定するとエラーをスローすることもありますが、すべてのコンパイラで発生するわけではありません。
Clang コンパイラでの 不完全型への逆参照ポインタ
エラー
これは、Clang コンパイラを介して同じコードを実行したときに発生します。
1790191360/source.c:6:5: error: incomplete type 'struct round' where a complete type is required
*x;
^
1790191360/source.c:5:12: note: forward declaration of 'struct round'
struct round *x = 0;
^
1790191360/source.c:6:5: warning: expression result unused [-Wunused-value]
*x;
^~
1 warning and 1 error generated.
ここで、コンパイラは不完全な構造体を呼び出しますが、参照解除されたポインターをエラーではなく警告として残します。
不完全な型へのポインターの参照解除
というエラーが発生する理由を理解するには、2つの概念を知る必要があります。
- 不完全型とは?
- ポインターの逆参照とはどういう意味ですか?
C の不完全な型
宣言されているが指定されていない型は不完全です (構造体型の場合)。
コンパイラが名前を別の名前に一致させるのを妨げる型名のタイプミスは、C 言語での不完全な型エラーの一般的な原因です (宣言を定義に一致させる場合など)。
不完全型を欠落型と考えるのは誤解です。 構造体の外でも不完全な型が発生する可能性があります。
不完全型の原因となる 3つのシナリオ:
- メンバーを持たない構造体型。
- メンバを持たない共用体型。
- 宣言されているが要素が挿入されていない配列。
不完全型を作成して定義する
同様の構造体または type
は、不完全な型を完成させるために不足している情報を指定する必要があります。
不完全なタイプの作成と完成は、以下の例で示されています。
構造体型を宣言しますが、メンバーを省略して不完全な構造体型を生成します。 この図の x
ポインターは library
という名前の不完全な構造体型を指しています。
struct library *x;
不完全な構造体型を完成させるために提供されたメンバーを使用して、後で同じスコープ内で同じ構造体型を宣言します。
struct library {
int book_id;
char book_name[50];
}
不完全な型の配列を作成するには、繰り返し回数を指定せずに配列型を宣言します。 例えば:
char book_name[]; /* book_name has incomplete type */
後で同じスコープ内で同じ名前を宣言し、その繰り返し回数を設定して、不完全な配列型を完成させます。
char book_name[25]; /* book_name now has complete type */
不完全な型がどのように完成されるかを理解したら、不完全な型への逆参照ポインタ
エラーを解決する 2 番目の部分に進むことができます。
C でのポインターの参照と逆参照
ポインターの機能は、値のアドレスを格納することです。つまり、何かへの参照を格納します。 ポインタが指すオブジェクトは、ポインティと呼ばれます。
C でのポインターの参照
ポインタとそれが指すポインティの割り当てには、2つの異なる段階があります。 ポインタ/ポインティ構造は、2つのレベルの操作を持っていると考えることができます。
機能させるには、両方のレベルですべてをセットアップする必要があります。 最もよくある間違いは、ポインタ レベルを操作するコードを書くことに集中し、ポインティ レベルの設定を怠ってしまうことです。
ポインティに接触しないポインタ操作は浅い
操作と呼ばれることがあり、接触するものは深い
操作と呼ばれます。
次のコードを検討してください。
ポインター ptr_a
は、main
関数内で作成されます。 このポインタは作成されますが、pointee
またはメモリ チャンクが割り当てられない限り、何かを格納することはできません。
メモリの割り当ては malloc
によって行われ、指定されたサイズはデータ型 int
と同等です。
void main() {
int* ptr_a; // Allocate the pointer
ptr_a = malloc(
sizeof(int)); // Allocate an int pointee, and set ptr_a to point to it
}
C++ コード:
int main() {
int* ptr_a; // Allocate the pointer ptr_a
ptr_a = new int; // Allocate an int pointee, and set ptr_a to point to it
}
C でのポインターの逆参照
逆参照操作は、ポインターで始まり、指示先まで続きます。 目的は、ポインティング先の状態を調べたり変更したりすることです。
ポインターは、ポインターがある場合にのみ逆参照できます。 ポインターがそれを指すようにする前に、pointee も割り当てる必要があります。 ポインティの設定を忘れることは、ポインタ プログラムで最もよくある間違いです。
ポインターを正常に逆参照できないコードの失敗は、最も一般的なランタイム クラッシュです。 Java のランタイム システムは、軽度の警告で不適切な逆参照の問題にフラグを立てます。
C や C++ などのコンパイル済み言語では、不適切な逆参照によってクラッシュが発生したり、自発的にメモリが破損したりする可能性があります。 これにより、コンパイルされた言語でポインターの問題を見つけるのが難しくなります。
C コード:
メモリが割り当てられ、ポインタ ptr_a
を指すようになると、逆参照によって値がメモリ内に格納されます。
void main() {
int* ptr_a;
ptr_a = malloc(
sizeof(int)); // Allocate an int pointee, and set ptr_a to point to it
*ptr_a = 42; // Dereference ptr_a to store 42 in its pointee
}
C++ コード:
int main() {
int* ptr_a; // Allocate the pointers
ptr_a = new int; // Allocate an int pointee, and set ptr_a to point to it
*ptr_a = 42; // Dereference ptr_a to store 42 in its pointee
}
C の共有ポインター
2つのポインターが同じポインティーに割り当てられている場合、それらは両方ともそこを指します。 その結果、y = x
の場合、y
は x
と同じポインティを指します。
ポインティは、ポインター割り当ての影響を受けません。
同じ参照を別のポインターと共有するように、1つのポインターのみを変更します。 ポインタの割り当て後、2つのポインタはポインティを共有
していると見なされます。
C コード:
void main() {
int* ptr_a;
int* ptr_b;
ptr_a = malloc(sizeof(int));
*ptr_a = 42;
*ptr_b = 13; // CRASH -- ptr_b does not have a pointee yet
ptr_b = ptr_a; // Pointer assignment sets ptr_b to point to ptr_a's pointee
*ptr_b = 13; // Dereference ptr_b to store 13 in its (shared) pointee
}
不完全な型への逆参照ポインター
エラーを解決する背後にある 2つの概念を理解しました。 エラーが発生したコードと問題の解決方法を見ていきます。
C の 不完全な型への逆参照ポインター
エラーを解決する
以下のプログラムには、整数メンバー length
を持つ構造体 rectangle
があります。 構造体の名前は、不完全な型を作成するために main
関数内の名前とは意図的に異なっています。
main
関数内で、ポインタ *a
が作成され、構造体 rectangle
サイズのメモリが割り当てられ、それがポイントされます。
次に、ポインターを使用して構造体メンバー length
を逆参照し、内部に値を格納します。 構造体は不完全な型であるため、不完全な型への逆参照ポインター
エラーをスローする必要があります。
#include <stdio.h>
#include <stdlib.h>
struct rectangle {
int length;
};
int main() {
struct square *a;
a = (struct rectngle *)malloc(sizeof(struct rectngle));
a->length = 33;
printf("%d", *a);
}
出力:
1647679200/source.c: In function 'main':
1647679200/source.c:10:38: error: invalid application of 'sizeof' to incomplete type 'struct rectngle'
a = (struct rectngle*)malloc(sizeof(struct rectngle));
^~~~~~
1647679200/source.c:11:3: error: dereferencing pointer to incomplete type 'struct square'
a->length = 33;
^~
この問題を解決するには、不完全な型 (この場合は構造体 rectngle
) を完成させる必要があります。
これらの変更は、不完全な構造体を完成させ、main
関数内のポインターを介してそのメンバーを逆参照するために行う必要があります。
#include <stdio.h>
#include <stdlib.h>
struct rectangle { // a struct rectngle is created
int length;
};
int main() {
struct rectangle *a; // name of struct inside main() is same as above struct
a = (struct rectangle *)malloc(sizeof(
struct rectangle)); /*pointer allocated memory and
pointer
to pointer 'a'. */
a->length = 33; // pointer deferenced.
printf("%d", *a); // value of pointer dereferenced
}
出力:
33
まとめ
この記事では、不完全な型へのポインターを逆参照することによって発生するエラーに光を当てます。 この記事を読み終えると、読者は簡単にポインターを参照および参照し、不完全な型を完成させることができます。