首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C++ 一維數組轉二維/三維數組:內存佈局映射與通用索引公式實現

     編輯:關於C語言

C/C++ 中多維數組在內存中按**行優先**連續存儲,一維數組可通過索引映射模擬多維訪問,無需額外內存分配。本文以 a[24] 為例,推導轉 b[4][6]、c[2][3][4] 的索引公式,給出生產級泛型實現,並驗證數據一致性。

核心原理:行優先線性化

  • 二維 [R][C]:邏輯 (r,c) → 物理索引 r * C + c。
  • 三維 [D][R][C]:邏輯 (d,r,c) → 物理索引 (d * R + r) * C + c = d * R * C + r * C + c。
  • 本質:編譯器為多維數組生成的正是這套計算,**手動映射等價於編譯器行為**,零開銷。

完整可運行示例 (C++11)

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <iomanip>

using namespace std;

// 將一維數組視為二維 [rows][cols] 訪問
void PrintAs2D(int* arr, int rows, int cols) {
    cout << "=== 二維視圖 [" << rows << "][" << cols << "] ===\n";
    for (int r = 0; r < rows; ++r) {
        for (int c = 0; c < cols; ++c) {
            int idx = r * cols + c;
            cout << '[' << r << "][" << c << "]=" << setw(3) << arr[idx] << ' ';
        }
        cout << '\n';
    }
}

// 將一維數組視為三維 [dep][rows][cols] 訪問
void PrintAs3D(int* arr, int dep, int rows, int cols) {
    cout << "\n=== 三維視圖 [" << dep << "][" << rows << "][" << cols << "] ===\n";
    for (int d = 0; d < dep; ++d) {
        for (int r = 0; r < rows; ++r) {
            for (int c = 0; c < cols; ++c) {
                int idx = (d * rows + r) * cols + c;
                cout << "a[" << d << "][" << r << "][" << c << "]=" << setw(3) << arr[idx] << ' ';
            }
            cout << '\n';
        }
        cout << '\n';
    }
}

int main() {
    const int TOTAL = 24;
    int* a = new int[TOTAL];

    // 填充隨機數
    srand(static_cast<unsigned>(time(nullptr)));
    for (int i = 0; i < TOTAL; ++i) a[i] = rand() % 100;

    cout << "原始一維數據:\n";
    for (int i = 0; i < TOTAL; ++i) cout << a[i] << ' ';
    cout << "\n\n";

    // 映射為 4×6 二維
    PrintAs2D(a, 4, 6);

    // 映射為 2×3×4 三維
    PrintAs3D(a, 2, 3, 4);

    // 驗證:修改三維視圖元素,一維數組同步變化
    int idx = (1 * 3 + 2) * 4 + 3;  // a[1][2][3]
    a[idx] = 999;
    cout << "\n修改 a[1][2][3]=999 後,一維數組對應位置: " << a[23] << '\n';

    delete[] a;
    return 0;
}

運行結果示例

原始一維數據:
83 86 77 15 93 35 86 92 49 21 62 27 90 59 63 26 40 26 72 36 11 68 67 29

=== 二維視圖 [4][6] ===
[0][0]= 83 [0][1]= 86 [0][2]= 77 [0][3]= 15 [0][4]= 93 [0][5]= 35
[1][0]= 86 [1][1]= 92 [1][2]= 49 [1][3]= 21 [1][4]= 62 [1][5]= 27
[2][0]= 90 [2][1]= 59 [2][2]= 63 [2][3]= 26 [2][4]= 40 [2][5]= 26
[3][0]= 72 [3][1]= 36 [3][2]= 11 [3][3]= 68 [3][4]= 67 [3][5]= 29

=== 三維視圖 [2][3][4] ===
a[0][0][0]= 83 a[0][0][1]= 86 a[0][0][2]= 77 a[0][0][3]= 15
a[0][1][0]= 93 a[0][1][1]= 35 a[0][1][2]= 86 a[0][1][3]= 92
a[0][2][0]= 49 a[0][2][1]= 21 a[0][2][2]= 62 a[0][2][3]= 27

a[1][0][0]= 90 a[1][0][1]= 59 a[1][0][2]= 63 a[1][0][3]= 26
a[1][1][0]= 40 a[1][1][1]= 26 a[1][1][2]= 72 a[1][1][3]= 36
a[1][2][0]= 11 a[1][2][1]= 68 a[1][2][2]= 67 a[1][2][3]= 29

修改 a[1][2][3]=999 後,一維數組對應位置: 999

通用映射模板 (現代 C++)

template <typename T>
class ArrayView {
    T* data_;
    std::vector<size_t> strides_;  // 步長數組
public:
    ArrayView(T* data, std::initializer_list<size_t> dims) : data_(data) {
        size_t stride = 1;
        for (auto it = dims.end() - 1; it != dims.begin() - 1; --it) {
            strides_.insert(strides_.begin(), stride);
            stride *= *it;
        }
    }
    // 任意維度索引訪問 (需配合參數包或 vector)
    T& operator()(std::initializer_list<size_t> idx) {
        size_t offset = 0;
        auto s = strides_.begin();
        for (size_t i : idx) offset += i * (*s++);
        return data_[offset];
    }
};

// 用法
ArrayView<int> view2d(a, {4, 6});
view2d({2, 3}) = 100;  // 等價 a[2*6+3]
ArrayView<int> view3d(a, {2, 3, 4});
view3d({1, 2, 3}) = 200;  // 等價 a[(1*3+2)*4+3]

應用場景與注意事項

場景說明
網絡/文件序列化多維數組扁平化傳輸,接收端按協議維度重構
GPU/計算庫互操作CUDA/OpenCL 核函數常要求一維指針,宿主端多維邏輯映射
避免動態多維分配開銷單次 new T[N] 替代多次 new,提升緩存局部性
舊代碼接口適配老 API 接收 int*,新邏輯用多維語義
  • 邊界檢查:手動映射無編譯器保護,建議封裝 ArrayView 內部 assert。
  • 對齊/填充:結構體數組映射時注意 alignas/#pragma pack。
  • 語義清晰:僅在「視圖」層面使用,核心邏輯優先用原生多維數組/std::vector/std::mdspan (C++23)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved