本文へスキップ
BecomeCoder

C言語コース · 第6章 ポインタとメモリ · レッスン24

ポインタと配列 ― 密接な関係

ブラウザで完結

導入

配列メモリ上で「箱を連続して並べたもの」だったことを思い出してください。実は配列名は、その先頭の箱の住所を表します。だからポインタと配列はとても近い関係にあります。

説明

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));

実際に動かしてみよう

下のエディタにCを書いて「実行」を押すと、ブラウザ内のインタプリタで出力が表示されます。本文の例をそのまま試したり、書き換えたりしてみましょう(scanf を使う回は「標準入力」欄に値を入れてから実行します)。

C/C++ — ブラウザ内で実行

ブラウザ内でC/C++を動かす学習用インタプリタ(JSCPP)を読み込みます(入門向けのサブセットです)。
スクロールして表示された時点でも自動で読み込まれます。