【C++】三次元配列入門!実例5選で完全マスター

C++で三次元配列を扱う方法を徹底解説するイメージC++
この記事は約15分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事では、プログラミング初心者から上級者までがC++での三次元配列の扱い方を完全に理解できるように、基本から応用までを詳細に解説します。

特に、初心者の方にも分かりやすいよう、基本概念の説明から始め、段階的により複雑な内容へと進めていきます。

三次元配列は、ゲーム開発、科学計算、データ分析など、多岐にわたる分野で活用されています。

この記事を通じて、あなたもその使い方をマスターし、C++のスキルを一層深めましょう。

●三次元配列の基本

三次元配列を理解するためには、まず配列とは何かを理解する必要があります。

配列とは、同じ型のデータを連続したメモリ領域に格納するデータ構造です。

C++では、これを非常に直感的に扱うことができます。

一次元配列はデータのリストとして、二次元配列は表やマトリックスとして想像することができますが、三次元配列は少し想像しにくいかもしれません。

三次元配列は、この二次元配列をさらに一段階深くしたものと考えることができます。

たとえば、立方体や3Dの空間をデータで表現したい場合に用いられます。

○三次元配列とは

三次元配列は、その名の通り、3つの次元を持つ配列です。

例えば、学校の各クラスにいる学生の成績を記録する場合、学年、クラス、学生という3つの次元でデータを整理することができます。

このように三次元配列は、より複雑なデータ構造を必要とする場面で役立ちます。

○三次元配列の宣言方法

三次元配列を宣言するには、基本的には一次元や二次元配列と同じ方法で、追加の次元を指定するだけです。

C++の構文を用いて三次元配列を宣言する基本形は下記のようになります。

データ型 配列名[次元1のサイズ][次元2のサイズ][次元3のサイズ];

ここで、「データ型」は配列に格納する値の型(例えばintやfloatなど)、それぞれの「次元」はその次元のサイズ、つまり配列の大きさを表します。

例えば、3学年、各学年に5クラス、各クラスに20人の学生がいる場合、その成績を記録する三次元配列は下記のように宣言できます。

int grades[3][5][20];

この配列は、「grades[0][1][2]」のように指定して、特定の学生の成績にアクセスすることができます。

この例では、1学年の2クラス目の3番目の学生の成績にアクセスしています。

●三次元配列の初期化

三次元配列を宣言した後、次の重要なステップは初期化です。

初期化とは、配列の各要素に特定の値を設定することを意味します。

これにより、配列が使用可能な状態になります。

C++では、様々な方法で三次元配列を初期化できますが、ここでは最も基本的な二つの方法を紹介します。

○サンプルコード1:基本的な初期化

最も単純な初期化方法は、配列宣言時にその要素に値を直接代入することです。

例えば、3×3×3の整数型三次元配列を0で初期化する場合、下記のように記述できます。

int array[3][3][3] = {0};

このコードは、arrayという名前の三次元配列のすべての要素を0で初期化します。

これは、配列の最初の要素にのみ0を指定し、残りの要素は自動的に0で初期化されるというC++の規則に基づいています。

しかし、すべての要素に異なる値を設定したい場合は、次のように各要素に値を直接指定する必要があります。

int array[3][3][3] = {
    {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}},
    {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}},
    {{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}
};

この方法では、配列の各「層」ごとに値をグループ化し、大括弧で囲んでいます。

このように、初期値を明示的に指定することで、配列の各要素が望む値で初期化されます。

○サンプルコード2:ループを使った初期化

配列の要素が多い場合や、動的に初期値を生成したい場合は、ループを使用して初期化する方法が効率的です。

下記のサンプルコードでは、3×3×3の整数型三次元配列を、ループを使用して連続する値で初期化しています。

int array[3][3][3];
int value = 1;

for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
        for (int k = 0; k < 3; ++k) {
            array[i][j][k] = value++;
        }
    }
}

このコードでは、三重のループを用いて配列の各要素にアクセスし、value変数を用いて連続する値を代入しています。

最初のループが最外層(第一次元)、次のループが中層(第二次元)、最後のループが最内層(第三次元)を処理します。

この方法により、柔軟かつ効率的に配列を初期化することが可能です。

●三次元配列のアクセス方法

三次元配列を扱う上で重要なのは、その要素へのアクセス方法です。

三次元配列の各要素にアクセスすることで、データの読み書きが可能になります。

C++では、配列のインデックスを使ってこの操作を行います。

インデックスは、配列の各次元における要素の位置を示す数字で、通常は0から始まります。

○サンプルコード3:要素へのアクセス

三次元配列の各要素にアクセスする基本的な方法を見ていきましょう。

下記のサンプルコードは、3×3×3の整数型三次元配列の各要素に順にアクセスし、その値を出力する例です。

int array[3][3][3] = {
    {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}},
    {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}},
    {{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}
};

for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
        for (int k = 0; k < 3; ++k) {
            std::cout << "array[" << i << "][" << j << "][" << k << "] = " << array[i][j][k] << std::endl;
        }
    }
}

このコードでは、三重のループを使用して、配列のすべての要素を順番に処理しています。

i, j, kはそれぞれ配列の第一次元、第二次元、第三次元のインデックスを表しており、各要素の値をstd::coutを使ってコンソールに出力しています。

○サンプルコード4:ループを使った要素の操作

次に、三次元配列の要素にループを使って操作を加える方法を見ていきます。

下記のサンプルコードでは、3×3×3の整数型三次元配列の各要素の値を2倍にしています。

int array[3][3][3];

for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
        for (int k = 0; k < 3; ++k) {
            array[i][j][k] = (i * j * k) * 2;
        }
    }
}

for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
        for (int k = 0; k < 3; ++k) {
            std::cout << "array[" << i << "][" << j << "][" << k << "] = " << array[i][j][k] << std::endl;
        }
    }
}

このコードでは、最初の三重のループで配列の各要素に値を代入し、次の三重のループでその値をコンソールに出力しています。

各要素には、そのインデックスの積に2を乗じた値が設定されています。

●三次元配列の応用例

三次元配列は、その構造が多様な応用を可能にします。

ここでは、三次元配列を使った二つの実践的な例を紹介します。

これらの例は、三次元配列の多様な使用方法を理解するのに役立ちます。

○サンプルコード5:三次元配列を使ったシミュレーション

三次元配列は、科学や工学の分野でシミュレーションによく使用されます。

たとえば、気候変動のシミュレーションや、建築物の設計などに役立ちます。

下記のサンプルコードは、簡単な空間シミュレーションの例を表しています。

const int width = 10;
const int height = 10;
const int depth = 10;
int space[width][height][depth];

// 空間に初期値を設定
for (int x = 0; x < width; ++x) {
    for (int y = 0; y < height; ++y) {
        for (int z = 0; z < depth; ++z) {
            space[x][y][z] = x + y + z;
        }
    }
}

// シミュレーション結果の出力
for (int x = 0; x < width; ++x) {
    for (int y = 0; y < height; ++y) {
        for (int z = 0; z < depth; ++z) {
            std::cout << "space[" << x << "][" << y << "][" << z << "] = " << space[x][y][z] << std::endl;
        }
    }
}

このコードは、10x10x10の三次元空間を表すspace配列を作成し、各セルにその座標の和を設定しています。

その後、この三次元空間の各セルの値を出力しています。

○サンプルコード6:グラフィックスデータの処理

三次元配列はグラフィックスの分野でも広く利用されます。

3Dグラフィックスのデータを操作する場合、三次元配列が非常に有用です。

下記のサンプルコードは、簡単な3Dグラフィックスデータの処理を模したものです。

const int width = 100;
const int height = 100;
const int depth = 100;
unsigned char image[width][height][depth];

// イメージデータの初期化
for (int x = 0; x < width; ++x) {
    for (int y = 0; y < height; ++y) {
        for (int z = 0; z < depth; ++z) {
            image[x][y][z] = static_cast<unsigned char>((x + y + z) % 256);
        }
    }
}

// 処理したデータの出力
for (int x = 0; x < width; ++x) {
    for (int y = 0; y < height; ++y) {
        for (int z = 0; z < depth; ++z) {
            std::cout << "image[" << x << "][" << y << "][" << z << "] = " << static_cast<int>(image[x][y][z]) << std::endl;
        }
    }
}

このコードでは、imageという名前の三次元配列を使用して、3Dイメージの各ピクセルに値を設定しています。

このように、三次元配列を使うことで、複雑な3Dデータを扱うことができます。

●注意点と対処法

C++における三次元配列の使用には、いくつかの注意点があります。

これらを理解し、適切な対処法を取ることで、プログラムのバグやパフォーマンスの問題を避けることができます。

○メモリ管理の注意点

三次元配列は、特に大きなサイズを持つ場合、多量のメモリを消費します。

C++では、大きな配列をスタックメモリ上に確保しようとすると、スタックオーバーフローを引き起こす可能性があります。

そのため、大きな配列はヒープメモリ上に確保することが一般的です。

これは、newキーワードを使用して動的にメモリを割り当てることで実現できます。

しかし、ヒープメモリを使用する際は、不要になったメモリをdeleteを使用して適切に解放することが重要です。

これを怠るとメモリリークが発生し、プログラムのパフォーマンスに影響を与える可能性があります。

○インデックスの範囲外アクセスの防止

三次元配列を使用する際には、配列の範囲外のインデックスにアクセスしないように注意する必要があります。

範囲外アクセスは、未定義の動作を引き起こし、プログラムがクラッシュする原因となります。

範囲外アクセスを防ぐためには、配列のサイズを超えるインデックスにアクセスしないようにループの条件を正しく設定することが重要です。

また、インデックスが負の値にならないようにすることも重要です。

例えば、配列のサイズをconst変数として保持し、ループの条件でこの変数を使用することで、インデックスが配列のサイズを超えないように制御できます。

●三次元配列の実践的なカスタマイズ方法

三次元配列の使用において、さらなる応用としてカスタマイズが可能です。

特定の用途や処理に合わせて、三次元配列を効率よく利用するための方法をいくつか見ていきましょう。

○カスタマイズ例1:動的なサイズ変更

三次元配列のサイズがプログラム実行時に変更される必要がある場合、動的配列を利用することができます。

この方法では、実行時に配列のサイズを指定し、必要に応じてメモリの割り当てや解放を行います。

下記のサンプルコードは、動的にサイズを設定する三次元配列の例です。

#include <iostream>
#include <vector>

int main() {
    int width, height, depth;
    std::cin >> width >> height >> depth;

    std::vector<std::vector<std::vector<int>>> array(width, std::vector<std::vector<int>>(height, std::vector<int>(depth)));

    // ここに処理を追加
    // 例えば、配列の要素に値を設定する

    return 0;
}

このコードでは、std::vectorを使って動的に三次元配列を生成しています。

std::cinを使用して、実行時に配列の各次元のサイズを入力することができます。

○カスタマイズ例2:多次元配列のラッパークラス

複雑な三次元配列の操作を簡単にするために、ラッパークラスを定義することが効果的です。

ラッパークラスを使用すると、配列の操作をカプセル化し、使いやすいインターフェースを提供できます。

ここでは、簡単な三次元配列のラッパークラスのサンプルを紹介します。

#include <vector>

class ThreeDimensionalArray {
private:
    std::vector<std::vector<std::vector<int>>> array;
    int width, height, depth;

public:
    ThreeDimensionalArray(int w, int h, int d) : width(w), height(h), depth(d), array(w, std::vector<std::vector<int>>(h, std::vector<int>(d))) {}

    void set(int x, int y, int z, int value) {
        if (x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z < depth) {
            array[x][y][z] = value;
        }
    }

    int get(int x, int y, int z) const {
        if (x >= 0 && x < width && y >= 0 && y < height && z >= 0 && z < depth) {
            return array[x][y][z];
        }
        return -1; // 例外やエラー処理をここに記述
    }

    // その他の便利なメソッドを追加
};

このラッパークラスでは、三次元配列に対する基本的な操作(値の設定や取得)をしています。

クラスを使用することで、配列の操作をより直感的でエラーの少ない方法で行うことができます。

まとめ

この記事では、C++における三次元配列の基本から応用、注意点、そしてカスタマイズ方法に至るまでを網羅的に解説しました。

これらの知識を活用することで、C++プログラミングにおける三次元配列の可能性を最大限に引き出し、より高度なプログラミング技術を身につけることができるでしょう。