ポインタ演算

ポインタの加算・減算・比較操作。配列走査に重要

概要

ポインタに対して、 加算や減算などの演算を行うことができます。

このポインタ演算は、 配列の走査や複雑なメモリ操作で極めて重要です。

ポインタの加算と減算

例1: ポインタ + 整数

#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr = arr;  // arr の最初の要素を指す
    
    std::cout << "*ptr: " << *ptr << std::endl;       // 10
    std::cout << "*(ptr+1): " << *(ptr + 1) << std::endl; // 20
    std::cout << "*(ptr+2): " << *(ptr + 2) << std::endl; // 30
    
    return 0;
}

*ptr: 10
*(ptr+1): 20
*(ptr+2): 30

重要ptr + 1は 「ptr から1個先」を意味します。 メモリ上ではsizeof(int)バイト先になります。

例2: ポインタの 加減演算

#include <iostream>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int* ptr = &arr[2];  // arr[2] を指す
    
    std::cout << *(ptr - 1) << std::endl;  // arr[1] = 2
    std::cout << *ptr << std::endl;       // arr[2] = 3
    std::cout << *(ptr + 1) << std::endl; // arr[3] = 4
    
    ptr++;  // ポインタを次の要素に移動
    std::cout << *ptr << std::endl;       // arr[3] = 4
    
    ptr--;  // ポインタを前の要素に戻す
    std::cout << *ptr << std::endl;       // arr[2] = 3
    
    return 0;
}

2
3
4
4
3

ポインタの比較

例:配列の要素を比較

#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr1 = &arr[1];
    int* ptr2 = &arr[3];
    
    std::cout << (ptr1 < ptr2) << std::endl;  // 1 (true)
    std::cout << (ptr1 > ptr2) << std::endl;  // 0 (false)
    std::cout << (ptr1 == ptr2) << std::endl; // 0 (false)
    
    return 0;
}

1
0
0

ポインタ演算の実践例

例1: ポインタを使った配列の走査

#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr = arr;
    
    // ポインタで配列を走査
    for (int i = 0; i < 5; i++) {
        std::cout << *ptr << " ";
        ptr++;  // 次の要素に移動
    }
    std::cout << std::endl;
    
    return 0;
}

10 20 30 40 50

例2: ポインタで反転

#include <iostream>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int* start = arr;
    int* end = arr + 4;  // 最後の要素
    
    while (start < end) {
        // 入れ替え
        int temp = *start;
        *start = *end;
        *end = temp;
        
        start++;
        end--;
    }
    
    // 表示
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

5 4 3 2 1

例3: ポインタで文字列を処理

#include <iostream>
#include <cstring>

int main() {
    const char* str = "Hello";
    
    // ポインタを使って各文字にアクセス
    for (const char* p = str; *p != '\0'; p++) {
        std::cout << *p << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

H e l l o

ポインタの差分

2つのポインタの間の距離を計算できます。

#include <iostream>

int main() {
    int arr[5] = {10, 20, 30, 40, 50};
    int* ptr1 = &arr[1];
    int* ptr2 = &arr[4];
    
    std::ptrdiff_t diff = ptr2 - ptr1;  // 距離を計算
    std::cout << "距離: " << diff << std::endl;  // 3
    
    return 0;
}

距離: 3

ポインタと効率性

ポインタ演算はメモリアクセスが効率的です。

例:2つの方法の比較

#include <iostream>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    // 方法1:インデックスアクセス
    for (int i = 0; i < 5; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
    
    // 方法2:ポインタアクセス(若干高速)
    for (int* p = arr; p < arr + 5; p++) {
        std::cout << *p << " ";
    }
    std::cout << std::endl;
    
    return 0;
}

1 2 3 4 5
1 2 3 4 5

ポインタと異なる型

#include <iostream>

int main() {
    double arr[3] = {1.5, 2.5, 3.5};
    double* ptr = arr;
    
    // ptr + 1 は double 4 バイト先
    std::cout << *ptr << std::endl;        // 1.5
    std::cout << *(ptr + 1) << std::endl;  // 2.5
    std::cout << *(ptr + 2) << std::endl;  // 3.5
    
    return 0;
}

1.5
2.5
3.5

ポインタ演算はデータ型のサイズを自動的に考慮します。

ポイント

  • ポインタ + n で、データ型のサイズを掛けた先の位置
  • ++/-- でポインタを次・前の要素に移動
  • ポインタの比較でメモリ上の位置を判定
  • ポインタの差分で要素数を計算
  • 型のサイズが自動計算されるため、直感的

よくある誤り

誤り1: 配列の範囲を超える

int arr[5] = {1, 2, 3, 4, 5};
int* ptr = arr + 10;  // 危険!: 範囲外
std::cout << *ptr << std::endl;  // 未定義動作

誤り2: 初期化されていないポインタで演算

int* ptr;  // 初期化されていない
ptr++;     // 危険!: ゴミ値

誤り3: 異なる配列のポインタを比較

int arr1[5], arr2[5];
if (arr1 < arr2) {  // メモリ上の位置の比較になる
    // ...
}

やってみよう

練習1: ポインタを使って配列をコピー。

練習2: ポインタを使って配列を反転。

練習3: 文字列(C形式)をポインタで処理。

チャレンジ: ポインタを使ったバイナリサーチの実装。

まとめ

  • ポインタ演算で配列を効率的に走査
  • データ型に応じた自動計算が便利
  • 範囲外アクセスに注意
  • C++の重要なテクニック