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

陣列維度計算進化史:從 C 風格巨集到 C++ 編譯期型別安全方案

     編輯:關於C++

問題起點:sizeof(arr)/sizeof(arr[0]) 的缺陷

傳統巨集:

#define DIM(a) (sizeof(a)/sizeof(a[0]))

兩大隱患:

  1. 接受非陣列:傳入 std::vector 等重載 operator[] 的物件,編譯器不報錯,產生無意義結果。
  2. 指標退化:傳入指標同樣編譯通過,但計算出的維度毫無意義。

第一次進化:利用下標運算子交換律

內建陣列支援 a[i] == i[a],但自定義 operator[] 不支援。修改巨集:

#define DIM(a) (sizeof(a)/sizeof(0[a]))

傳入 vector 時 0[vec] 編譯失效,解決問題一。但指標問題依然存在。

第二次進化:函式樣板 + 陣列參考推導維度


template 
inline size_t DimensionOf(T (&arr)[N]) { return N; }
  • 參數為 陣列參考,指標無法綁定 → 編譯錯誤。
  • N 由編譯器自動推導,零成本。
  • 但回傳值為 size_t 執行期值,非編譯期常數,無法用於 int arr2[DimensionOf(arr)] 或樣板參數。

第三次進化:sizeof 編譯期求值 + 輔助結構體


template 
struct dimension_help_struct { unsigned char uc[N]; };

template 
const dimension_help_struct& make_dimension_help_struct(T (&arr)[N]);

#define DIM(a) (sizeof(make_dimension_help_struct(a).uc))
  • make_dimension_help_struct 僅需宣告,無需定義(sizeof 不求值)。
  • 回傳 dimension_help_struct 參考,.uc 陣列大小為 N,sizeof 得 N。
  • 全編譯期運算,零執行期開銷,結果為編譯期常數。
  • 需確保結構體 1 位元組對齊(unsigned char uc[N] 保證)。

終極簡化:輔助函式直接回傳陣列參考


template 
unsigned char (&dimension_help_fun(T (&arr)[N]))[N];

#define DIM(a) (sizeof(dimension_help_fun(a)))
  • 省去輔助結構體,直接回傳 unsigned char (&)[N] 陣列參考。
  • sizeof 作用於該陣列參考,得 N。
  • 測試通過:VS2008、GCC 4.1.2。

使用示範


int is[] = {1, 2, 3};
int* pi = is;
std::vector vi;

DIM(is);        // 3 ✓
DIM(vi);        // 編譯錯誤 ✓
DIM(pi);        // 編譯錯誤 ✓

// 編譯期常數用法
int is2[DIM(is)];          // ✓
std::array a; // ✓ (C++11)

總結與心得

  1. 型別安全:利用 C++ 型別系統(陣列參考、樣板推導)在編譯期排除指標/容器。
  2. 零成本抽象:全編譯期運算,無執行期開銷。
  3. 演進歷程:從巨集 → 樣板函式 → 編譯期 sizeof 技巧 → 極簡輔助函式,橫跨 3 年,見證 C++ 型別系統的表達力與複雜度。

現代 C++(C++17 起)可直接使用 std::size(arr)(<iterator>),但理解演進過程有助於掌握樣板元編程核心思想。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved