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

C 語言多維數組底層實現:行主序佈局、地址計算與通用容器封裝

     編輯:關於C語言

多維數組內存佈局

C 語言多維數組採用行主序:最後一維連續,倒數第二維由最後一維構成,以此類推。

二維數組 `int a[3][4]`

內存地址遞增方向 →
a[0][0] a[0][1] a[0][2] a[0][3] | a[1][0] a[1][1] a[1][2] a[1][3] | a[2][0] ...

元素 `a[i][j]` 地址:`base + (i * COLS + j) * sizeof(int)`

三維數組 `int a[5][4][10]`

`a[i][j][k]` 地址:

offset = (i * 4 * 10 + j * 10 + k) * sizeof(int)
      = (i * (D2*D3) + j * D3 + k) * sizeof(int)

通用 N 維地址計算公式

維度 `D1, D2, ..., Dn`,索引 `j1, j2, ..., jn`(0 基):

offset = (((j1 * D2 + j2) * D3 + j3) * D4 + ... + jn) * sizeof(element)
// 等價於:
stride[n-1] = 1
stride[k]   = stride[k+1] * D_{k+2}   (倒推)
offset = Σ (j_k * stride[k])

通用多維數組容器實現

支援運行時確定維度與大小,封裝創建、訪問、銷毀:


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define MAX_DIM 8

typedef struct {
    int ndim;           // 維數
    int *dims;          // 各維大小 [ndim]
    int *strides;       // 步長 [ndim] (以元素為單位)
    int *data;          // 扁平化數據
    size_t total;       // 總元素數
} MultiArray;

int marray_init(MultiArray *a, int ndim, ...) {
    if (ndim < 1 || ndim > MAX_DIM) return -1;
    a->ndim = ndim;

    a->dims = malloc(ndim * sizeof(int));
    a->strides = malloc(ndim * sizeof(int));
    if (!a->dims || !a->strides) return -1;

    va_list ap;
    va_start(ap, ndim);
    a->total = 1;
    for (int i = 0; i < ndim; i++) {
        int d = va_arg(ap, int);
        if (d <= 0) { va_end(ap); return -1; }
        a->dims[i] = d;
        a->total *= d;
    }
    va_end(ap);

    // 計算步長(行主序)
    a->strides[ndim - 1] = 1;
    for (int i = ndim - 2; i >= 0; i--)
        a->strides[i] = a->strides[i + 1] * a->dims[i + 1];

    a->data = calloc(a->total, sizeof(int));
    return a->data ? 0 : -1;
}

int marray_set(MultiArray *a, int value, ...) {
    va_list ap;
    va_start(ap, value);
    size_t idx = 0;
    for (int i = 0; i < a->ndim; i++) {
        int coord = va_arg(ap, int);
        if (coord < 0 || coord >= a->dims[i]) { va_end(ap); return -1; }
        idx += coord * a->strides[i];
    }
    va_end(ap);
    a->data[idx] = value;
    return 0;
}

int marray_get(MultiArray *a, ...) {
    va_list ap;
    va_start(ap, a);
    size_t idx = 0;
    for (int i = 0; i < a->ndim; i++) {
        int coord = va_arg(ap, int);
        if (coord < 0 || coord >= a->dims[i]) { va_end(ap); return -1; }
        idx += coord * a->strides[i];
    }
    va_end(ap);
    return a->data[idx];
}

void marray_destroy(MultiArray *a) {
    free(a->dims);
    free(a->strides);
    free(a->data);
    a->dims = a->strides = a->data = NULL;
}

// 打印演示(僅 2D)
void marray_print_2d(MultiArray *a) {
    if (a->ndim != 2) return;
    for (int i = 0; i < a->dims[0]; i++) {
        for (int j = 0; j < a->dims[1]; j++)
            printf("%4d ", a->data[i * a->strides[0] + j]);
        printf("\n");
    }
}

使用示例


int main(void) {
    MultiArray a;
    marray_init(&a, 2, 4, 6);  // 4行6列

    int val = 1;
    for (int i = 0; i < 4; i++)
        for (int j = 0; j < 6; j++)
            marray_set(&a, val++, i, j);

    marray_print_2d(&a);
    // 輸出:
    //    1    2    3    4    5    6
    //    7    8    9   10   11   12
    //   13   14   15   16   17   18
    //   19   20   21   22   23   24

    printf("a[2][3] = %d\n", marray_get(&a, 2, 3));  // 16

    marray_destroy(&a);
    return 0;
}

關鍵設計點

  • 步長預計算:構造時計算 `strides`,訪問時 O(ndim) 累加,無乘法開銷
  • 邊界檢查:每維座標驗證,防止越界
  • 類型泛化:`data` 可改為 `void*` + `elem_size`,支援任意類型
  • 對比 VLA:C99 VLA (`int a[n][m]`) 棧分配、大小受限、不可跨函數返回;本實現堆分配、靈活、可封裝

編譯器優化視角

靜態多維數組 `int a[3][4]` 編譯器可展開地址計算為常量;動態容器需間接訪問。熱點路徑建議:

  • 展開內層循環手動計算指針遞增
  • 使用 `restrict` 關鍵字(C99)消除別名
  • 考慮 SSE/AVX 向量化(需連續內存,行主序天然滿足)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved