ポインタとメモリ

C・C++のポインタ演算

このレッスンでは、Cのポインタ演算とC++のイテレーターで、配列の要素を順にたどります。

はじめに

ポインタに1を足しても、1バイト進むのではなく配列の次の要素に進みます。コンパイラが型の大きさをもとに計算してくれるからです。そのため、ポインタを使って配列のような連続したメモリ領域をたどれます。

大切なポイント

  • ポインタは、まだ存在している正しい型のオブジェクトを指すか、nullにしておきましょう。
  • 確保したメモリ領域は、ちょうど1回だけ解放しましょう。
  • どの関数がメモリの持ち主(ownership)で、どのデータが読み取り専用(const)かをはっきりさせましょう。

CとC++のコード例

C
実行 →
main.c
#include <stdio.h>

int main(void) {
    int values[] = {10, 20, 30, 40};
    int sum = 0;
    for (int *p = values; p < values + 4; ++p) sum += *p;
    printf("sum=%d distance=%td\n", sum, (values + 4) - values);
    return 0;
}
実行結果
sum=100 distance=4
C++
実行 →
main.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector values{10, 20, 30, 40};
    int sum = 0;
    for (auto it = values.begin(); it != values.end(); ++it) sum += *it;
    std::cout << "sum=" << sum << " distance="
              << std::distance(values.begin(), values.end()) << '\n';
}
実行結果
sum=100 distance=4

CとC++の比較

ループはポインタpが最初の要素を指す状態から始まり、pがvalues + 4の位置に達するまで進みます。各ステップで*pが今の値を取り出し、合計に足します。ポインタ演算が有効なのは同じ配列の範囲内か、最後の要素のすぐ後ろまでだけです。C++のイテレーターを使えば、いろいろな種類のコンテナを同じようにたどれます。

C

メモリの持ち主と解放は、すべてプログラマーが自分で保証します。

C++

コンテナとRAIIが、オブジェクトの寿命が終わるときにリソースを自動で解放します。

発展問題

両方のバージョンを実行し、コードを書き換えて、それぞれの言語がもたらす安全性の違いを観察しましょう。

  • nullポインタやメモリ確保の失敗で試しましょう。
  • AddressSanitizerとUndefinedBehaviorSanitizerを付けて実行し、メモリのエラーを見つけましょう。
  • どのポインタがメモリの持ち主で、どのポインタが読むだけかをはっきり書き添えましょう。

チャレンジで練習する

このレッスンの内容を使って実際の問題を解きましょう。各チャレンジはブラウザで動き、すぐにテストされ、AI のレビューも受けられます。

すべてのチャレンジ