C・C++の多次元配列
このレッスンでは、2次元の行列をたどり、行ごとの要素の合計を求めます。
はじめに
2次元配列は、行と列からなる表のようにイメージできます。CでもC++でも、2次元配列はメモリ上に行ごとの順番(row-major)で連続して保存されます。つまり、1行目がまるごと先に保存され、その後に2行目が続きます。
大切なポイント
- ポインタは、まだ存在している正しい型のオブジェクトを指すか、nullにしておきましょう。
- 確保したメモリ領域は、ちょうど1回だけ解放しましょう。
- どの関数がメモリの持ち主(ownership)で、どのデータが読み取り専用(const)かをはっきりさせましょう。
動きで理解する
セルを指すと、その行と列、そしてメモリ上の位置がわかります。メモリには行が順番に並んで保存されています。
CとC++のコード例
C
main.c
#include <stdio.h>
int main(void) {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
for (int row = 0; row < 2; ++row) {
int total = 0;
for (int column = 0; column < 3; ++column) total += matrix[row][column];
printf("row %d=%d\n", row, total);
}
return 0;
}
row 0=6
row 1=15
C++
main.cpp
#include <array>
#include <iostream>
int main() {
std::array<std::array<int, 3>, 2> matrix{{{{1, 2, 3}}, {{4, 5, 6}}}};
for (std::size_t row = 0; row < matrix.size(); ++row) {
int total = 0;
for (int value : matrix[row]) total += value;
std::cout << "row " << row << '=' << total << '\n';
}
}
row 0=6
row 1=15
CとC++の比較
入れ子になった2つのループが行列をたどり、外側のループは各行を、内側のループはその行の要素を変数totalに足します。Cで2次元配列を関数に渡すときは、最初の次元以外のすべての次元の大きさを書く必要があります。C++のstd::arrayは大きさを型そのものに持ち、インデックスが有効かを確認するat()メソッドがあります。
C
メモリの持ち主と解放は、すべてプログラマーが自分で保証します。
C++
コンテナとRAIIが、オブジェクトの寿命が終わるときにリソースを自動で解放します。
発展問題
両方のバージョンを実行し、コードを書き換えて、それぞれの言語がもたらす安全性の違いを観察しましょう。
- nullポインタやメモリ確保の失敗で試しましょう。
- AddressSanitizerとUndefinedBehaviorSanitizerを付けて実行し、メモリのエラーを見つけましょう。
- どのポインタがメモリの持ち主で、どのポインタが読むだけかをはっきり書き添えましょう。
チャレンジで練習する
このレッスンの内容を使って実際の問題を解きましょう。各チャレンジはブラウザで動き、すぐにテストされ、AI のレビューも受けられます。
- Row Sums Add up every row of a grid. 簡単
- Minesweeper Neighbor Counts Annotate a rectangular board. 中級