導入
配列がメモリ上で「箱を連続して並べたもの」だったことを思い出してください。実は配列名は、その先頭の箱の住所を表します。だからポインタと配列はとても近い関係にあります。
説明
int a[3] = {10, 20, 30}; は、メモリ上に int 3個ぶん(4バイト×3)の連続した区画を作り、先頭から順に値を置きます。配列名 a は、その先頭要素の住所(=&a[0])と同じ意味を持ちます。
flowchart LR
subgraph MEM["int a[3] = {10, 20, 30}; のメモリ配置"]
direction LR
e0["a[0] = 10<br/>2000番地"]
e1["a[1] = 20<br/>2004番地"]
e2["a[2] = 30<br/>2008番地"]
end
a["配列名 a = 2000(先頭の住所)"] --> e0
先頭の住所さえ分かれば、あとは「1つ隣=4バイト先」とたどっていけます。ポインタに配列を入れて、*(p + i) で i 番目にアクセスできます。
#include <stdio.h>
int main() {
int a[3] = {10, 20, 30};
int *p = a; // 配列名 = 先頭の住所(&a[0] と同じ)
printf("%d\n", *p); // 10(先頭)
printf("%d\n", *(p + 1)); // 20(1つ次)
printf("%d\n", *(p + 2)); // 30
return 0;
}
p + 1 は「次の要素の住所」を指します。ここが賢いところで、int なら自動的に4バイト、double なら8バイト進みます(型が分かっているので、箱いくつぶん進めばよいか計算してくれる)。*(p + i) は a[i] と同じ結果になります。実は a[i] という書き方は、内部的には *(a + i) の省略形なのです。
flowchart LR p0["p<br/>→ a[0]"] -->|"p + 1"| p1["p+1<br/>→ a[1]"] -->|"p + 1"| p2["p+2<br/>→ a[2]"]
配列の全要素を、ポインタをたどりながら合計する例です。
#include <stdio.h>
int main() {
int a[4] = {2, 4, 6, 8};
int *p = a;
int total = 0;
for (int i = 0; i < 4; i++) {
total += *(p + i); // a[i] と同じ
}
printf("合計 %d\n", total); // 20
return 0;
}
やってみよう
a の中身を変えて、*(p + i) で各要素が取り出せることを確かめましょう。a[i] と *(p + i) が同じ結果になること、&a[0] と a が同じ番地になることも見比べてください。
演習
int a[3] = {5, 10, 15}; の先頭を指すポインタ p を作り、*(p + 2) の値を表示してください(結果は 15)。
ヒント1を見る
int *p = a; のあと printf("%d\n", *(p + 2));。