
【6日でできるC言語入門】ポインタ変数としての配列変数
C言語において、配列とポインタは密接な関係を持っています。一見すると異なる機能を持つように見えますが、実は配列変数は「ポインタの特殊な形」と捉えることもできます。ここでは、配列変数とポインタ変数の関係について、具体的なプログラム例や図表を使ってわかりやすく解説します。これを理解することで、より柔軟で効率的なC言語プログラミングができるようになります。

1.配列変数とポインタ変数の基本
1.1. 配列変数とポインタ変数の関係
C言語では、配列の名前は「配列の先頭アドレス」を示すため、配列変数はポインタ変数のようにも利用できます。
以下は例となるプログラムです。
サンプルプログラム1:配列の先頭アドレスの代入
プロジェクト/ファイル名: Lesson54_1/main.c
#include <stdio.h>
int main(void) {
int numbers[4] = { 5, 10, 15, 20 };
int* ptr = NULL;
ptr = numbers; // 配列の先頭アドレスをポインタに代入
for (int i = 0; i < 4; i++) {
printf("numbers[%d]=%d, *(ptr+%d)=%d, ptr[%d]=%d\n",
i, numbers[i], i, *(ptr + i), i, ptr[i]);
}
return 0;
}出力結果
numbers[0]=5, *(ptr+0)=5, ptr[0]=5
numbers[1]=10, *(ptr+1)=10, ptr[1]=10
numbers[2]=15, *(ptr+2)=15, ptr[2]=15
numbers[3]=20, *(ptr+3)=20, ptr[3]=20このように、配列変数とポインタ変数は、アクセスの仕方が同じであることが分かります。
1.2. 配列とポインタのアドレス・値の関係(表で理解)
| 配列表現 | アドレス表現 | ポインタによる表現 | ポインタによる値 |
|---|---|---|---|
| numbers[0] | &numbers[0] | ptr | *ptr |
| numbers[1] | &numbers[1] | ptr+1 | *(ptr+1) |
| numbers[2] | &numbers[2] | ptr+2 | *(ptr+2) |
| numbers[3] | &numbers[3] | ptr+3 | *(ptr+3) |
2.配列変数とポインタ変数の違い
2.1. 配列変数とポインタ変数の本質的な違い
配列変数は「特定のメモリ領域」を表し、その領域の先頭アドレスは固定されています。一方、ポインタ変数は「どこのアドレスでも参照可能」で、自由に指す先を変更できます。
サンプルプログラム2:配列変数の代入の禁止例
プロジェクト/ファイル名: Lesson54_2/main.c
#include <stdio.h>
int main(void) {
int a[3] = {1, 2, 3};
int b[3];
b = a; // これはコンパイルエラーとなる
return 0;
}出力結果
重大度レベル コード 説明
エラー (アクティブ) E0137 式は変更可能な左辺値である必要があります
エラー C2106 '=': 左のオペランドが、左辺値になっていません。 解説
配列同士の直接代入はできません。配列の先頭アドレスは固定されているため、b = a のような代入は許されません。
2.2. ポインタ変数の柔軟性
ポインタ変数は、参照する先を動的に切り替えられます。また、ポインタの値(アドレス)をインクリメントすることで、配列の各要素を順に参照することも可能です。
サンプルプログラム3:ポインタのインクリメント
プロジェクト/ファイル名: Lesson54_3/main.c
#include <stdio.h>
int main(void) {
char str[4] = { 'A', 'B', 'C', '\0' };
char* p = str;
while (*p != '\0') {
printf("%c ", *p);
p++; // ポインタのインクリメント
}
printf("\n");
return 0;
}出力結果
A B C図:ポインタのインクリメントのイメージ
str(char配列): | 'A' | 'B' | 'C' | '\0' |
↑ ↑ ↑
p p+1 p+23.ポインタ変数と配列変数の相互利用
3.1. ポインタ変数を配列のように使う
ポインタ変数に配列の先頭アドレスを代入すれば、ptr[i] のような配列表現が可能です。
サンプルプログラム4:ポインタ変数の配列表現
プロジェクト/ファイル名: Lesson54_4/main.c
#include <stdio.h>
int main(void) {
float data[3] = { 1.1, 2.2, 3.3 };
float* fp = data;
for (int i = 0; i < 3; i++) {
printf("fp[%d]=%.1f *(fp+%d)=%.1f\n", i, fp[i], i, *(fp + i));
}
return 0;
}出力結果
fp[0]=1.1 *(fp+0)=1.1
fp[1]=2.2 *(fp+1)=2.2
fp[2]=3.3 *(fp+2)=3.33.2. 配列変数をポインタのように使う
逆に、配列変数そのものも、ポインタとして利用できます。
例: *data、*(data+1) など。
4.注意点と応用
4.1. 配列変数でできないこと
配列変数はポインタ変数と同じように見えても、「他の変数のアドレスを指す」ことや「代入で配列同士を入れ替える」ことはできません。
それに対して、ポインタ変数は柔軟にアドレスを変更できます。
4.2. 応用:ポインタで部分配列を扱う
ポインタを利用すれば、配列の一部分だけを参照することも簡単です。
サンプルプログラム5:部分配列参照
プロジェクト/ファイル名: Lesson54_5/main.c
#include <stdio.h>
int main(void) {
int arr[5] = { 10, 20, 30, 40, 50 };
int* p = &arr[2]; // 配列の3番目の要素を指す
for (int i = 0; i < 3; i++) {
printf("%d ", *(p + i));
}
printf("\n");
return 0;
}出力結果
30 40 50まとめ
C言語では、配列変数とポインタ変数は非常に密接な関係を持っています。配列変数は「ポインタの特殊形」と考えると理解しやすく、両者は多くの点で似たように扱うことができます。しかし、配列変数には「代入不可」などの制約があり、ポインタ変数のほうが柔軟性が高いという違いもあります。この違いをしっかり把握することで、より効率的なプログラム設計が可能となります。
