🔗 ポインタと配列の切っても切れない関係

プログラム

C言語において、配列名は、その配列の先頭要素のアドレスを示す定数ポインタとして扱われます。この関係性を理解することが、ポインタ操作の効率的な利用に繋がります。

1. 配列名とアドレスの関係 📍

配列名自体が、配列の最初の要素を指すポインタです。

コード例 1: 配列名のアドレスとポインタへの代入

#include <stdio.h>

int main(void) {
    int arr[3] = {10, 20, 30};
    int *p;

    // 1. 配列名 'arr' をポインタに代入
    p = arr;

    // 2. 配列の先頭要素のアドレス '&arr[0]' をポインタに代入
    // p = &arr[0]; // (この行と上の一行は完全に同じ意味)

    printf("arrのアドレス(配列名): %p\n", arr);
    printf("arr[0]のアドレス (&arr[0]): %p\n", &arr[0]);
    printf("ポインタ p の値: %p\n", p);

    return 0;
}

実行結果(アドレスは環境により異なります):

arrのアドレス(配列名): 0x7ffee03f6980
arr[0]のアドレス (&arr[0]): 0x7ffee03f6980
ポインタ p の値: 0x7ffee03f6980
  • 配列名 arr先頭要素のアドレス &arr[0] は、完全に同じメモリ上の場所を指しています。
  • そのため、ポインタ変数 p には、単純に p = arr; と記述するだけでアドレスが代入できます。

2. 配列へのアクセス方法の違い 🎯

配列の要素にアクセスする方法は、配列の添字(インデックス)を使う方法と、ポインタの演算を使う方法の2通りがあります。

コード例 2: 添字アクセスとポインタアクセス

#include <stdio.h>

int main(void) {
    int arr[3] = {10, 20, 30};
    int *p = arr;

    // 1. 添字を使ったアクセス
    printf("arr[1] の値: %d\n", arr[1]);

    // 2. ポインタ演算を使ったアクセス
    printf("*(p + 1) の値: %d\n", *(p + 1));

    // 3. 配列名を使ったポインタアクセス(添字アクセスと同じ)
    printf("*(arr + 1) の値: %d\n", *(arr + 1));

    return 0;
}

実行結果:

arr[1] の値: 20
*(p + 1) の値: 20
*(arr + 1) の値: 20

🧠 ポインタ演算の仕組み

ポインタ演算では、ポインタに整数を足すと、「そのデータ型一つ分のサイズ」だけアドレスが移動します。

  • *(p + 1) は、p が指すアドレスから、int型(通常4バイト)1つ分先に移動した場所の値を参照するという意味です。
  • これは arr[1] と全く同じ要素を指します。一般的に、arr[i]*(arr + i) は等価です。

3. ポインタによる配列の巡回 (イテレーション) 🔄

ポインタをインクリメント(++)することで、配列の要素を順番に処理できます。これは、ループ処理において非常に効率的な手法です。

コード例 3: ポインタを使った配列の巡回

#include <stdio.h>

int main(void) {
    int arr[] = {10, 20, 30, 40, 50};
    // ポインタpを配列の先頭に設定
    int *p = arr; 
    // 配列の要素数を計算
    int size = sizeof(arr) / sizeof(arr[0]);

    printf("ポインタで配列を巡回:\n");

    // 配列の最後までループを回す
    for (int i = 0; i < size; i++) {
        // 現在のポインタが指す値を出力
        printf("値: %d, アドレス: %p\n", *p, p);
        
        // ポインタを次の要素のアドレスに進める
        p++; 
    }

    // p は現在、配列の末尾(50の次)を指している
    printf("ループ後のポインタ p: %p\n", p);

    return 0;
}
  • ループ内で p++ とすると、pint型ポインタであるため、アドレスが次の int型要素の先頭に移動します。
  • この方法で配列の全要素に順番にアクセスできます。

💡 重要な違い:配列名とポインタ

特徴配列名 (arr)ポインタ変数 (p)
定数ポインタ変数
変更可否代入やインクリメントはできない (arr++ はコンパイルエラー)代入やインクリメントができる (p++ は有効)

配列名自体は定数であるため、arr = &arr[1];arr++; のような操作はできません。しかし、配列のアドレスを代入したポインタ変数 p は、自由に操作(p++ など)が可能です。

ポインタと配列の関係性は、「配列名=配列の先頭アドレスを保持する定数ポインタ」と覚えると、理解が深まります。

コメント