C++: ポインタと配列

最終更新:2026-08-31

レッスン16で配列を学びましたが、実は配列とポインタには深い関係があります。

実は、配列名はポインタ —— 配列の先頭要素のアドレスを指します。

この関係を理解すると、配列を「ポインタ視点」で扱えるようになります。


1. 配列とポインタの関係

(1) 1.1 基本的な関係

配列名はポインタです。

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    
    std::cout << "arr = " << arr << std::endl;
    std::cout << "&arr[0] = " << &arr[0] << std::endl;
    // 同じアドレスが出力される! arr と &arr[0] は同じ
    
    return 0;
}

出力例:

TEXT 📖 参照専用
arr = 0x7ffd4a3b
&arr[0] = 0x7ffd4a3b
💡 ヒント: arr&arr[0]同じアドレスを指します。



2. ポインタで配列にアクセス

(1) 2.1 基本的な使い方

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr; // pは配列の先頭を指す
    
    // 方法1:添字演算子(推奨)
    std::cout << "arr[0] = " << p[0] << std::endl; // 10
    std::cout << "arr[1] = " << p[1] << std::endl; // 20
    
    // 方法2:ポインタ演算
    std::cout << "*p = " << *p << std::endl; // 10
    std::cout << "*(p+1) = " << *(p+1) << std::endl; // 20
    
    return 0;
}
💡 ヒント: p[i]*(p+i) は等価です(ポインタ演算)。



3. ポインタ演算

(1) 3.1 ポインタの加算

ポインタに整数を加えると、アドレス値に n × sizeof(型) バイト加算されます。

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr; // arr[0]を指す
    
    std::cout << "p = " << p << std::endl; // 仮に 0x1000
    std::cout << "p + 1 = " << p + 1 << std::endl; // 0x1004(0x1001ではない!)
    std::cout << "p + 2 = " << p + 2 << std::endl; // 0x1008
    
    return 0;
}

解説:

  • int は4バイト
  • p + 11 × 4 = 4 バイト加算
  • p + 1arr[1] を指す

(2) 3.2 ポインタの減算

同じ配列内の2つのポインタの差は、要素数(インデックスの差)になります。

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p1 = &arr[0]; // arr[0]を指す
    int* p2 = &arr[3]; // arr[3]を指す
    
    std::cout << "p2 - p1 = " << p2 - p1 << std::endl; // 3(要素数の差)
    
    return 0;
}
💡 ヒント: 同じ配列内のポインタ同士でのみ意味を持ちます。



4. ポインタで配列を走査(反復)

▶ サンプル 1:配列の走査(難易度 ⭐)

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    
    // 方法1:添字(初心者向け)
    std::cout << "方法1(添字):" << std::endl;
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    // 方法2:ポインタ
    std::cout << "方法2(ポインタ):" << std::endl;
    for (int* p = arr; p < arr + 5; p++) {
        std::cout << *p << " ";
    }
    std::cout << std::endl;
    
    // 方法3:std::begin/end(推奨)
    // std::cout << "方法3:std::begin/end" << std::endl;
    // for (int* p = std::begin(arr); p != std::end(arr); p++) {
    //     std::cout << *p << " ";
    // }
    
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
方法1(添字):
10 20 30 40 50
方法2(ポインタ):
10 20 30 40 50 
💡 ヒント: 通常は方法1(添字)を使いましょう。ポインタ走査は内部動作を理解するのに役立ちます。



5. 配列を関数に渡す

(1) 5.1 問題:配列はポインタとして渡される

配列を関数に渡すと、配列はポインタに変換されます(先頭要素へのポインタ)。

CPP
#include <iostream>

void printSize(int arr[]) {
    // arrはポインタ、配列ではない!
    std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 8(64ビット、ポインタのサイズ)
}

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    
    std::cout << "sizeof(arr) = " << sizeof(arr) << std::endl; // 20(5 × 4 = 20バイト)
    printSize(arr);
    
    return 0;
}
💡 ヒント: printSize 関数内の arr はポインタなので、sizeof(arr) はポインタのサイズ(8バイト)になり、配列全体のサイズ(20バイト)にはなりません。


(2) 5.2 解決策:配列サイズを渡す

CPP
#include <iostream>

// サイズを別途渡す
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    printArray(arr, 5); // サイズを渡す(推奨)
    return 0;
}
💡 ヒント: または std::arraystd::vector を使うと、サイズ情報も一緒に渡せます。



6. 実践例:配列の反転

▶ サンプル 2:ポインタで配列を反転(難易度 ⭐⭐)

CPP
#include <iostream>

void reverseArray(int* arr, int size) {
    int* left = arr; // 先頭を指す
    int* right = arr + size - 1; // 末尾を指す
    
    while (left < right) {
        // leftとrightの値を交換
        int temp = *left;
        *left = *right;
        *right = temp;
        
        left++; // 左から右へ
        right--; // 右から左へ
    }
}

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    
    std::cout << "反転前:";
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    reverseArray(arr, 5);
    
    std::cout << "反転後:";
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
反転前:10 20 30 40 50
反転後:50 40 30 20 10


7. よくあるエラー

(1) 7.1 ポインタの範囲外アクセス

CPP
#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    std::cout << *(p + 10) << std::endl; // ❌ 範囲外!p+10は配列外
    // 未定義動作、予測不能な結果
    
    return 0;
}
💡 ヒント: C++はポインタの境界チェックをしない!コンパイルは通りますが、重大なバグになります。


(2) 7.2 無関係なポインタ同士の減算

CPP
#include <iostream>

int main() {
    int x = 5;
    int y = 10;
    int* p1 = &x;
    int* p2 = &y;
    
    std::cout << p2 - p1 << std::endl; // ❌ 未定義動作!p1とp2は同じ配列内にない
    // 意味のない結果
    
    return 0;
}

▶ サンプル 3:ポインタで配列サイズを計算(難易度 ⭐⭐)

CPP
#include <iostream>

int main() {
    int data[] = {5, 10, 15, 20, 25};

    // ポインタ演算で配列サイズを計算
    int* begin = data;
    int* end = data + 5;  // 最後の要素の次

    size_t size = end - begin;
    std::cout << "配列サイズ: " << size << std::endl;

    // ポインタで逆順に反復
    std::cout << "逆順: ";
    for (int* p = end - 1; p >= begin; p--) {
        std::cout << *p << " ";
    }
    std::cout << std::endl;

    return 0;
}
▶ 試してみよう

出力:

TEXT 📖 参照専用
配列サイズ: 5
逆順: 25 20 15 10 5

❓ よくある質問

Q arr&arr は値が同じだけど型が違う?
A はい!

  • arr の型は int*int へのポインタ)
  • &arr の型は int(*)[5](「5つのintの配列」へのポインタ)

値は同じ(先頭アドレス)ですが、型が違う

Q sizeof(arr)main では20なのに、関数では8なのはなぜ?
A main では arr は配列として扱われますが、関数内では arr はポインタとして扱われます。

解決策:


📖 まとめ

  • 配列名はポインタ(先頭要素のアドレス)
  • ポインタの加算は、n × sizeof(型) バイト進む
  • ポインタの減算は、要素数の差を返す
  • 配列を関数に渡すとポインタになる
  • ポインタで配列を操作:添字、ポインタ演算、標準関数など

📝 練習問題

  1. 初級(難易度 ⭐): int 配列 arr[5] = {1, 2, 3, 4, 5} を作り、int* ポインタ parr を指してください。ポインタで全要素を出力してください。

  2. 中級(難易度 ⭐⭐): 関数 int* findElement(int* arr, int size, int target) を書いてください。配列から値を検索し、見つかったらそのポインタを返す、なければ nullptr を返す。

  3. 上級(難易度 ⭐⭐⭐): プログラムを書いてください。ポインタで「バブルソート」を実装:

    • 配列を入力
    • 昇順にソート
    • ソート前後を表示


8. 🚀 次のステップ

ポインタと配列の関係を理解したら、次は ポインタ応用(レッスン24) —— ポインタの詳細な型、void* ポインタ、ポインタ配列を学ぼう!

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%