ポインタとメモリ

C・C++でconstを正しく使う

このレッスンでは、キーワードconstで読み取り専用のデータを宣言し、ポインタとconstの組み合わせ方の違いを理解します。

はじめに

キーワードconstを使うと、関数は受け取ったデータを変更しないと約束できます。constの付いたデータを誰かがうっかり変更すると、コンパイラがすぐにエラーを出します。これでコードが安全になり、読む人もどの関数がデータを変更できるのかがわかりやすくなります。

大切なポイント

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

CとC++のコード例

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

int sum(const int *values, size_t count) {
    int total = 0;
    for (size_t i = 0; i < count; ++i) total += values[i];
    return total;
}

int main(void) {
    const int values[] = {2, 4, 6};
    printf("%d\n", sum(values, 3));
    return 0;
}
実行結果
12
C++
実行 →
main.cpp
#include <array>
#include <iostream>

int sum(const std::array<int, 3>& values) {
    int total = 0;
    for (int value : values) total += value;
    return total;
}

int main() {
    const std::array values{2, 4, 6};
    std::cout << sum(values) << '\n';
}
実行結果
12

CとC++の比較

引数const int *valuesは、関数sumが配列の要素を読むだけで変更しないことを示します。宣言は変数名から外側へ読むとわかりやすいです。constデータへのポインタ(const int *)は指している値を守り、constポインタ(int * const)は別のアドレスを指せません。C++にはさらにconstメンバー関数があり、オブジェクトの状態を変えないことを約束します。

C

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

C++

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

発展問題

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

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