[C++] 2次元配列をポインタで使う方法をわかりやすく解説
C++で2次元配列をポインタで扱う方法は、配列のメモリ構造を理解することが重要です。
2次元配列は実際には連続したメモリ領域に格納されており、ポインタを使ってアクセスできます。
例えば、int arr[3][4];
の場合、arr
は最初の行の先頭アドレスを指します。
ポインタを使う場合、int (*p)[4] = arr;
のように行ポインタを定義し、p[i][j]
で要素にアクセスします。
また、動的に確保する場合はnew
を使い、int**
型のポインタを用いる方法もあります。
2次元配列とポインタの基本
C++における2次元配列は、配列の配列として表現されます。
ポインタを使うことで、メモリの効率的な管理や動的な配列の操作が可能になります。
ここでは、2次元配列とポインタの基本的な概念を解説します。
2次元配列の定義
2次元配列は、行と列を持つ配列です。
以下のように定義できます。
#include <iostream>
using namespace std;
int main() {
// 3行2列の静的2次元配列を定義
int array[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
// 配列の要素を出力
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
return 0;
}
1 2
3 4
5 6
ポインタを使った2次元配列の扱い
ポインタを使うことで、2次元配列の要素にアクセスすることができます。
以下のようにポインタを使った例を示します。
#include <iostream>
using namespace std;
int main() {
// 3行2列の静的2次元配列を定義
int array[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
// ポインタを使って配列の要素にアクセス
int (*ptr)[2] = array; // 2次元配列のポインタ
// ポインタを使って要素を出力
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
cout << ptr[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
return 0;
}
1 2
3 4
5 6
2次元配列のメモリ構造
2次元配列はメモリ上で連続して配置されます。
各行は連続したメモリ領域に格納され、ポインタを使ってそのアドレスを操作することができます。
これにより、動的なメモリ管理が可能になります。
2次元配列とポインタを組み合わせることで、C++におけるメモリ管理が効率的に行えます。
ポインタを使うことで、配列の要素に直接アクセスでき、柔軟なプログラミングが可能になります。
静的配列をポインタで扱う方法
静的配列は、コンパイル時にサイズが決定される配列です。
ポインタを使用することで、静的配列の要素にアクセスしたり、操作したりすることができます。
ここでは、静的配列をポインタで扱う方法について解説します。
静的配列の定義とポインタの初期化
まず、静的配列を定義し、そのポインタを初期化する方法を見てみましょう。
#include <iostream>
using namespace std;
int main() {
// 5要素の静的配列を定義
int array[5] = {10, 20, 30, 40, 50};
// 配列の先頭アドレスをポインタに代入
int* ptr = array; // arrayは配列の先頭アドレスを指すポインタ
// ポインタを使って要素を出力
for (int i = 0; i < 5; i++) {
cout << *(ptr + i) << " "; // ポインタを使って要素にアクセス
}
cout << endl; // 改行
return 0;
}
10 20 30 40 50
ポインタを使った要素の変更
ポインタを使って静的配列の要素を変更することもできます。
以下の例では、ポインタを使って配列の要素を変更しています。
#include <iostream>
using namespace std;
int main() {
// 5要素の静的配列を定義
int array[5] = {10, 20, 30, 40, 50};
// 配列の先頭アドレスをポインタに代入
int* ptr = array; // arrayは配列の先頭アドレスを指すポインタ
// ポインタを使って要素を変更
*(ptr + 2) = 100; // 3番目の要素を100に変更
// 変更後の要素を出力
for (int i = 0; i < 5; i++) {
cout << *(ptr + i) << " "; // ポインタを使って要素にアクセス
}
cout << endl; // 改行
return 0;
}
10 20 100 40 50
ポインタと配列の関係
ポインタと配列は密接に関連しています。
配列名はその配列の先頭要素のアドレスを指すため、ポインタを使って配列の要素にアクセスすることができます。
この特性を利用することで、配列の操作がより柔軟になります。
静的配列をポインタで扱うことで、配列の要素に直接アクセスしたり、変更したりすることが可能です。
ポインタを使うことで、配列の操作が効率的になり、プログラムの柔軟性が向上します。
動的に確保した2次元配列の扱い方
C++では、new
演算子を使用して動的にメモリを確保することができます。
これにより、プログラムの実行時に必要なサイズの2次元配列を作成することが可能になります。
ここでは、動的に確保した2次元配列の扱い方について解説します。
動的2次元配列の作成
まず、動的に2次元配列を作成する方法を見てみましょう。
以下の例では、new
を使って2次元配列を動的に確保しています。
#include <iostream>
using namespace std;
int main() {
int rows = 3; // 行数
int cols = 2; // 列数
// 動的に2次元配列を確保
int** array = new int*[rows]; // 行のポインタを確保
for (int i = 0; i < rows; i++) {
array[i] = new int[cols]; // 各行に列の配列を確保
}
// 配列に値を代入
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = (i + 1) * (j + 1); // 値を代入
}
}
// 配列の要素を出力
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] array[i]; // 各行のメモリを解放
}
delete[] array; // 行のポインタのメモリを解放
return 0;
}
1 2
2 4
3 6
動的2次元配列のメモリ管理
動的に確保した2次元配列は、使用後に必ずメモリを解放する必要があります。
delete
演算子を使用して、確保したメモリを解放します。
上記の例では、各行のメモリを解放した後、行のポインタ自体のメモリも解放しています。
これにより、メモリリークを防ぐことができます。
ポインタを使った要素のアクセス
動的に確保した2次元配列の要素には、ポインタを使ってアクセスすることもできます。
以下の例では、ポインタを使って要素にアクセスし、値を変更しています。
#include <iostream>
using namespace std;
int main() {
int rows = 3; // 行数
int cols = 2; // 列数
// 動的に2次元配列を確保
int** array = new int*[rows];
for (int i = 0; i < rows; i++) {
array[i] = new int[cols];
}
// 配列に値を代入
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = (i + 1) * (j + 1);
}
}
// ポインタを使って要素を変更
int* ptr = &array[1][0]; // 2行目の先頭要素のポインタ
*ptr = 99; // 変更
// 配列の要素を出力
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] array[i];
}
delete[] array;
return 0;
}
1 2
99 4
3 6
動的に確保した2次元配列は、実行時に必要なサイズを柔軟に扱うことができ、ポインタを使って要素にアクセスすることが可能です。
メモリ管理を適切に行うことで、効率的なプログラミングが実現できます。
関数で2次元配列を扱う方法
C++では、関数を使って2次元配列を扱うことができます。
配列を引数として渡すことで、関数内で配列の要素にアクセスしたり、操作したりすることが可能です。
ここでは、関数で2次元配列を扱う方法について解説します。
静的2次元配列を関数に渡す
まず、静的に定義された2次元配列を関数に渡す方法を見てみましょう。
以下の例では、2次元配列を引数として受け取り、その要素を出力する関数を定義しています。
#include <iostream>
using namespace std;
// 2次元配列を引数に取る関数
void printArray(int array[3][2]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
}
int main() {
// 3行2列の静的2次元配列を定義
int array[3][2] = {
{1, 2},
{3, 4},
{5, 6}
};
// 関数を呼び出して配列を出力
printArray(array);
return 0;
}
1 2
3 4
5 6
動的2次元配列を関数に渡す
次に、動的に確保した2次元配列を関数に渡す方法を見てみましょう。
以下の例では、動的に確保した2次元配列を引数として受け取り、その要素を出力する関数を定義しています。
#include <iostream>
using namespace std;
// 動的2次元配列を引数に取る関数
void printDynamicArray(int** array, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
}
int main() {
int rows = 3; // 行数
int cols = 2; // 列数
// 動的に2次元配列を確保
int** array = new int*[rows];
for (int i = 0; i < rows; i++) {
array[i] = new int[cols];
}
// 配列に値を代入
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = (i + 1) * (j + 1);
}
}
// 関数を呼び出して配列を出力
printDynamicArray(array, rows, cols);
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] array[i];
}
delete[] array;
return 0;
}
1 2
2 4
3 6
2次元配列を返す関数
C++では、2次元配列を返す関数を作成することもできますが、ポインタを使って動的に確保した配列を返す必要があります。
以下の例では、動的に2次元配列を生成し、それを返す関数を定義しています。
#include <iostream>
using namespace std;
// 動的に2次元配列を生成する関数
int** createDynamicArray(int rows, int cols) {
int** array = new int*[rows];
for (int i = 0; i < rows; i++) {
array[i] = new int[cols];
}
return array; // 配列のポインタを返す
}
int main() {
int rows = 3; // 行数
int cols = 2; // 列数
// 関数を呼び出して動的2次元配列を生成
int** array = createDynamicArray(rows, cols);
// 配列に値を代入
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
array[i][j] = (i + 1) * (j + 1);
}
}
// 配列の要素を出力
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << array[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] array[i];
}
delete[] array;
return 0;
}
1 2
2 4
3 6
関数を使って2次元配列を扱うことで、配列の要素にアクセスしたり、操作したりすることができます。
静的配列と動的配列の両方に対応した関数を定義することで、柔軟なプログラミングが可能になります。
2次元配列とポインタの応用例
2次元配列とポインタを組み合わせることで、さまざまなプログラミングのシナリオに対応できます。
ここでは、2次元配列とポインタを活用したいくつかの応用例を紹介します。
1. 行列の加算
2次元配列を使って行列の加算を行うプログラムの例です。
ポインタを使って配列の要素にアクセスし、結果を新しい配列に格納します。
#include <iostream>
using namespace std;
// 行列の加算を行う関数
void addMatrices(int** matrixA, int** matrixB, int** result, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
result[i][j] = matrixA[i][j] + matrixB[i][j]; // 要素の加算
}
}
}
int main() {
int rows = 2; // 行数
int cols = 2; // 列数
// 動的に2次元配列を確保
int** matrixA = new int*[rows];
int** matrixB = new int*[rows];
int** result = new int*[rows];
for (int i = 0; i < rows; i++) {
matrixA[i] = new int[cols];
matrixB[i] = new int[cols];
result[i] = new int[cols];
}
// 行列Aの値を代入
matrixA[0][0] = 1; matrixA[0][1] = 2;
matrixA[1][0] = 3; matrixA[1][1] = 4;
// 行列Bの値を代入
matrixB[0][0] = 5; matrixB[0][1] = 6;
matrixB[1][0] = 7; matrixB[1][1] = 8;
// 行列の加算
addMatrices(matrixA, matrixB, result, rows, cols);
// 結果を出力
cout << "Result of Matrix Addition:" << endl;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
cout << result[i][j] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] matrixA[i];
delete[] matrixB[i];
delete[] result[i];
}
delete[] matrixA;
delete[] matrixB;
delete[] result;
return 0;
}
Result of Matrix Addition:
6 8
10 12
2. 2次元配列の転置
2次元配列の転置を行うプログラムの例です。
ポインタを使って元の配列の要素を新しい配列にコピーします。
#include <iostream>
using namespace std;
// 行列の転置を行う関数
void transposeMatrix(int** matrix, int** transposed, int rows, int cols) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
transposed[j][i] = matrix[i][j]; // 転置
}
}
}
int main() {
int rows = 2; // 行数
int cols = 3; // 列数
// 動的に2次元配列を確保
int** matrix = new int*[rows];
int** transposed = new int*[cols];
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols];
}
for (int j = 0; j < cols; j++) {
transposed[j] = new int[rows];
}
// 行列の値を代入
matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
// 行列の転置
transposeMatrix(matrix, transposed, rows, cols);
// 転置結果を出力
cout << "Transposed Matrix:" << endl;
for (int j = 0; j < cols; j++) {
for (int i = 0; i < rows; i++) {
cout << transposed[j][i] << " "; // 要素を出力
}
cout << endl; // 行の区切り
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] matrix[i];
}
for (int j = 0; j < cols; j++) {
delete[] transposed[j];
}
delete[] matrix;
delete[] transposed;
return 0;
}
Transposed Matrix:
1 4
2 5
3 6
3. 2次元配列の探索
2次元配列内の特定の値を探索するプログラムの例です。
ポインタを使って配列の要素にアクセスし、指定した値が存在するかどうかを確認します。
#include <iostream>
using namespace std;
// 2次元配列内の値を探索する関数
bool searchValue(int** matrix, int rows, int cols, int value) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == value) {
return true; // 値が見つかった場合
}
}
}
return false; // 値が見つからなかった場合
}
int main() {
int rows = 2; // 行数
int cols = 3; // 列数
// 動的に2次元配列を確保
int** matrix = new int*[rows];
for (int i = 0; i < rows; i++) {
matrix[i] = new int[cols];
}
// 行列の値を代入
matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
// 値を探索
int valueToFind = 5;
if (searchValue(matrix, rows, cols, valueToFind)) {
cout << valueToFind << " は行列内に存在します。" << endl;
} else {
cout << valueToFind << " は行列内に存在しません。" << endl;
}
// メモリの解放
for (int i = 0; i < rows; i++) {
delete[] matrix[i];
}
delete[] matrix;
return 0;
}
5 は行列内に存在します。
2次元配列とポインタを活用することで、行列の加算、転置、探索など、さまざまな応用が可能です。
これらの技術を使うことで、より複雑なデータ構造やアルゴリズムを実装することができます。
まとめ
この記事では、C++における2次元配列とポインタの基本的な使い方から、応用例まで幅広く解説しました。
特に、静的および動的に確保した2次元配列の扱い方や、関数を通じた操作方法、行列の加算や転置、特定の値の探索といった具体的な例を通じて、実践的な知識を提供しました。
これらの技術を活用して、より複雑なデータ構造やアルゴリズムに挑戦してみてください。