一次元配列のメモリ上の配置
#include <stdio.h>
int main() {
int numbers[8];
int index;
for (index = 0; index < 8; index++) {
printf("int型のサイズ: %zu
", sizeof(int));
printf("&numbers[%d] = %p
", index, &numbers[index]);
}
return 0;
}
int型のサイズ: 4
&numbers[0] = 0x7ffc12345678
int型のサイズ: 4
&numbers[1] = 0x7ffc1234567c
int型のサイズ: 4
&numbers[2] = 0x7ffc12345680
int型のサイズ: 4
&numbers[3] = 0x7ffc12345684
int型のサイズ: 4
&numbers[4] = 0x7ffc12345688
int型のサイズ: 4
&numbers[5] = 0x7ffc1234568c
int型のサイズ: 4
&numbers[6] = 0x7ffc12345690
int型のサイズ: 4
&numbers[7] = 0x7ffc12345694
上記の出力から、int型が4バイトを占有し、要素 numbers[index+1] のアドレスが numbers[index] のアドレスに sizeof(int) を加えた値と等しいことがわかります。これは、一次元配列の要素がスタック領域に連続して格納されていることを示しています。
次に、ポインタを使用して配列要素にアクセスする方法を見ていきましょう。
方法1:配列のアドレスを直接ポインタで参照する
#include <stdio.h>
int main() {
int data[5];
int *ptr, index;
printf("配列dataの要素を入力してください: ");
for (index = 0; index < 5; index++) {
scanf("%d", &data[index]);
}
for (index = 0; index < 5; index++) {
ptr = &data[index];
printf("ptr = %d ", *ptr);
printf("data[%d] = %d
", index, data[index]);
}
return 0;
}
注意:ポインタは要素のアドレスを指します。配列名はメモリ上の配列の先頭アドレスを表します。ptr = data; は ptr = &data[0]; と等価です。
方法2:ポインタに配列名を代入してアクセスする
#include <stdio.h>
int main() {
int values[5] = {10, 20, 30, 40, 50};
int *pointer, idx;
for (idx = 0; idx < 5; idx++) {
pointer = values;
printf("pointer = %d ", *pointer);
printf("*values = %d ", *values);
printf("*(pointer + 1) = %d ", *(pointer + 1));
printf("*pointer + 1 = %d ", *pointer + 1);
printf("values[%d] = %d
", idx, values[idx]);
}
return 0;
}
出力:
出力から、*pointer が values[0] に等しく、*(pointer + 1) が values[1] に等しいことがわかります。
pointer = 10 *values = 10 *(pointer + 1) = 20 *pointer + 1 = 11 values[0] = 10
pointer = 10 *values = 10 *(pointer + 1) = 20 *pointer + 1 = 11 values[1] = 20
pointer = 10 *values = 10 *(pointer + 1) = 20 *pointer + 1 = 11 values[2] = 30
pointer = 10 *values = 10 *(pointer + 1) = 20 *pointer + 1 = 11 values[3] = 40
pointer = 10 *values = 10 *(pointer + 1) = 20 *pointer + 1 = 11 values[4] = 50
*(pointer + 1) と *pointer + 1 の違い
* の優先順位は + よりも高いです。したがって、*pointer + 1 はポインタが指す値(*pointer)に1を加算した結果です。一方、*(pointer + 1) は、まずポインタを1つ進め(アドレスを sizeof(int) 分だけ増やし)、その後でデリファレンス(間接参照)を行います。つまり、配列の次の要素を取得します。
関係性:*(pointer + i) は values[i] と等価です。また、pointer は values と等価です。これらの関係から、values[i] は *(values + i) と書くこともでき、ポインタを用いて配列要素にアクセスする方法が理解できます。