歴史的にポインタはC言語におけるメモリ操作の基盤となり、配列の要素や動的構造体への効率的なアクセスのための柔軟なメカニズムを提供しました。しかし、配列へのポインタとポインタの配列の構文や意味論は、しばしば混乱を引き起こします。
問題:初心者プログラマは、配列へのポインタ(pointer to array)とポインタの配列(array of pointers)をしばしば混同し、メモリの誤った使用、パラメータの渡し方のエラー、および構文エラーの原因となります。
解決策:
宣言と使用の例:
// intの配列へのポインタ: int (*p)[10]; int arr[10]; p = &arr; // intへのポインタの配列 int *ap[10]; for (int i = 0; i < 10; ++i) { ap[i] = &arr[i]; } // 配列へのポインタを使って要素を取得する: (*p)[2] = 5; // arrの3番目の要素 // ポインタの配列を使って値を取得する: *ap[2] = 8; // apを通じてarrの3番目の要素
主な特徴:
**int p[10]とint (p)[10]は同じですか?
いいえ。int *p[10]はintのポインタの配列です。int (*p)[10]は10個のintの配列へのポインタです。括弧がないと大きな混乱が生じます!
コードの例:
int arr[10]; int *p[10]; // ポインタの配列 int (*q)[10] = &arr; // 配列へのポインタ
*intへの通常のポインタをint (p)[10]型の変数に自由に代入できますか?
いいえ。通常のint *は1つの要素を指しますが、int (*p)[10]は10個の整数の配列を指します。型は明示的にキャストしない限り互換性がありません。
2次元配列を関数に正しく渡すにはどうすればよいですか?
第2の次元のサイズを明示的に指定する必要があります:
void foo(int a[][4], int n); // 4要素のn行の配列
または配列へのポインタを使用する:
void bar(int (*a)[4], int n);
エンジニアが変数をint *p[10]として宣言し、&arrに代入しようとして、arrがint arr[10]である理由でアクセスを配列として得ようとし、コンパイルエラーまたは無効な動作を受け取ります。
利点:
欠点:
開発者が括弧を慎重に使用する:int (*p)[10]しっかりと違いを理解し、配列を関数に正しく渡し、typedefを使用して宣言を簡素化します。
利点:
欠点: