C++ static_assert 與 assert 的差異

本篇 ShengYu 來介紹 C++ static_assert 與 assert 的差異,assert 是 C/C++ 都有的巨集,static_assert 是 C++11 才新增的,

assert 的用法

assert 會在執行時間測試判斷陳述句,這邊來複習一下 assert 的用法,assert() 是如果它的條件陳述句回傳 false 的話會輸出錯誤訊息到 stderr 然後呼叫 abort 結束程式執行,

assert 最常是使用在檢查空指標 assert(p != NULL)
這邊我們示範是檢查 n 變數數值的大小,用來確保程式執行到這個時間點時數值應該是我們所預期的範圍,

C 要用 assert 需引用 <assert.h>,C++ 要用 assert 請引用 <cassert>
要關閉 assert 的話,在引用 <cassert> 前定義 #define NDEBUG,assert 就會被關閉,

cpp-assert.cpp
1
2
3
4
5
6
7
8
9
10
// g++ cpp-assert.cpp -o a.out
#include <iostream>
#include <cassert>

int main() {
int n = 5;
assert(n > 10);
std::cout << n << "\n";
return 0;
}

執行時期的錯誤輸出如下,

1
2
cpp-assert: cpp-assert.cpp:9: int main(): Assertion `n > 10' failed.
Aborted (core dumped)

在 C++ 11 前靜態檢查的作法

這邊舉個最常會用到靜態檢查的例子,就是檢查 struct 的大小,就是我要確保程式在別的平台下 struct AAA 的大小也要是 8,但是用 assert 的話是執行到 assert 這一行才判斷的,如下,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// g++ cpp-assert2.cpp -o a.out
#include <iostream>
#include <cassert>

struct AAA {
int a; // 4 bytes
int b; // 4 bytes
}; // 8 bytes

int main() {
assert(sizeof(struct AAA) == 8);
std::cout << sizeof(struct AAA) << "\n";
return 0;
}

可是 struct 的大小這件事是在編譯時期就可以知道的事,為什麼不能在編譯時期檢查提早發現呢?

所以這邊利用了前置處理器的技巧來達成這件事,在 C++ 11 之前就會是這樣子寫,

cpp-assert3.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// g++ cpp-assert3.cpp -o a.out
#include <iostream>
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]

struct AAA {
int a; // 4 bytes
int b; // 4 bytes
}; // 8 bytes

int main() {
C_ASSERT(sizeof(struct AAA) == 8);
std::cout << sizeof(struct AAA) << "\n";
return 0;
}

看見上方的 #define C_ASSERT 的範例,我們可以瞭解在前置處理階段時先來判斷陳述句,

如果改成 C_ASSERT(sizeof(struct AAA) == 10) 的話,就會出現下面這樣的編譯錯誤訊息,

1
2
3
4
5
6
7
cpp-assert3.cpp: In function ‘int main()’:
cpp-assert3.cpp:3:55: error: size of array ‘__C_ASSERT__’ is negative
#define C_ASSERT(e) typedef char __C_ASSERT__[(e)?1:-1]
^
cpp-assert3.cpp:11:5: note: in expansion of macro ‘C_ASSERT’
C_ASSERT(sizeof(struct AAA) == 10);
^

這樣就達成了編譯時期的靜態檢查,除了上述的使用場合,也常使用來

  • 檢查兩個 struct C_ASSERT(sizeof(struct_a) == sizeof(struct_b))
  • 檢查兩個 array C_ASSERT(sizeof(arrar1) == sizeof(array2))
  • 宣告的緩衝大小是否超過路徑的最大長度 C_ASSERT(buffer_size <= MAX_PATH)
    等等這些情形。

C++11 把 static_assert 納入標準後,我們就不需要使用各平台或各函式庫各自實作的 static assert,使得更容易地跨平台開發。

C++ 11 static_assert 的用法

C++11 的 static_assert 宣告會在編譯時期測試判斷陳述句,
由於編譯器已知 struct AAA 結構的大小,因此會立即計算運算式的值。

cpp-static_assert.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
// g++ cpp-static_assert.cpp -o a.out -std=c++11
#include <iostream>

struct AAA {
int a; // 4 bytes
int b; // 4 bytes
}; // 8 bytes

int main() {
static_assert(sizeof(struct AAA) == 8, "struct AAA size is not 8 bytes");
std::cout << sizeof(struct AAA) << "\n";
return 0;
}

執行結果為,

1
8

如果把改成 sizeof(struct AAA) == 10呢?編譯時期就會報錯,錯誤輸出如下,

1
2
3
4
cpp-static_assert.cpp: In function ‘int main()’:
cpp-static_assert.cpp:10:5: error: static assertion failed: struct AAA size is not 8 bytes
static_assert(sizeof(struct AAA) == 10, "struct AAA size is not 8 bytes");
^

總結

總結一下,static_assert 是 compile time 編譯時期來檢查陳述句 statement,而 assert 是 run time 執行時期來檢查陳述句 statement。

以上就是 C++ static_assert 與 assert 的差異介紹,
如果你覺得我的文章寫得不錯、對你有幫助的話記得 Facebook 按讚支持一下!

其它相關文章推薦
C/C++ 新手入門教學懶人包
C/C++ 字串轉數字的4種方法
C++ virtual 的兩種用法
C/C++ 字串反轉 reverse
C/C++ call by value傳值, call by pointer傳址, call by reference傳參考 的差別
C++ 類別樣板 class template
std::sort 用法與範例
std::find 用法與範例
std::queue 用法與範例
std::map 用法與範例