這篇簡單來記錄一下 C++11 加入、代表特定大小的整數型別(Fixed width integer types)。
一般來說,在 C++ 裡面我們要使用整數型別的變數的時候,大致上會根據數值上的需求,宣告成 char、short、int、long、long long;而如果不考慮負數的話,則可以在前面加上 unsigned、變成無號整數。而其他也還有像是 short int、signed short 這類的用法。
但是這邊有一個問題,那就是上面這些型別到底用了多少記憶體?
char 由於代表的是字元,一般都是 8bit 沒有太大的問題;而 short 以此類推應該是 16bit,但是 int、long、long long 呢?理論上應該是二進位的形式越來越大,所以是 32、64、128 嗎?但是現在 C++ 真的有 128bit 的整數嗎?當然沒有。
實際上,C++ 的標準裡面針對這些基礎的整數型別,只有定義他們最少要有幾 bit,而並沒有嚴格規範實際上是多少;實際上這些型別要使用多少記憶體、數值範圍多大,是會根據實作而不同的。
在《Fundamental types》這篇有列出標準的規範、以及一些實際的狀況;而 Heresy 這邊也用現在 64 位元的 Visual Studio 2022 和 Ubuntu 22 的 g++ 12.1 測試的一下,狀況如下:
|
Type
|
C++ Standard
|
Visual C++ 2022
|
g++ 12.1
|
|---|---|---|---|
|
char
|
8 bit
|
8 bit
|
8 bit
|
|
short
|
16 bit
|
16 bit
|
16 bit
|
|
int
|
16 bit
|
32 bit
|
32 bit
|
|
long
|
32 bit
|
32 bit
|
64 bit
|
|
long long
|
64 bit
|
64 bit
|
64 bit
|
恩,滿訝異的是,和 Heresy 過去認知的不太一樣了,原來 int 不見得會比 short 大?!
而這邊也可以看到,兩個不同的平台上,對於 long 的實作是不同的;而實際上,比較早期的 Win16 API 似乎就真的會變成 short 和 int 同樣是 16 bit。
實際上,C++ 標準是要求這幾種型別的大小要符合下面的狀況:
1 == sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)
通常這邊的差異不會有太大的問題,但是如果是要以 binary 的形式來做資料的儲存、傳遞,或是跨系統、或是其他需要嚴格資料大小的情境,這邊的不同可能就會造成嚴重的問題了!
實際上,在 C99 的時候,C 語言就透過 <stdint.h> 定義了固定大小的整數型別(Fixed width integer type、參考);而在 C++11 的時候,也正式加入了對應的 <cstdint>,針對這部分做出支援(參考)。
這邊主要是定義了:int8_t、int16_t、int32_t、int64_t 這種明確標示出用了多少記憶體空間的型別。
同時,對於無號整數的部分,也有 uint8_t、uint16_t、uint32_t、uint64_t。
透過這種型別,基本上就可以更明確地宣告特定記憶體量的整數型別變數了。
而如果只是想要用系統中可以使用的最大整數型別的話,則也可以使用 intmax_t / uintmax_t;一般來說應該都會是 int64_t 就是了。
另外,這邊個人覺得比較有趣的,是在有號整數的部分,在 C++ Reference 上都是標記成「optional」的?
除了上面這些比較直覺的型別定義之外,這邊其實也還有一些有趣的型別。
像是 int_fastN_t / uint_fastN_t 這類的型別,則是要使用至少有 N bits、處理速度最快的型別;如果很在意速度的話,或許可以透過使用這種型別,讓編譯器來決定要用多大的記憶體。
以 int_fast16_t 來說,在 Visual Studio 裡面測試是 32 bit、在 Ubuntu 的 g++ 則直接跳到 64 bit 了。
而 int_leastN_t / uint_leastN_t 則是代表至少要有 N bits、使用記憶體最小的型別。
不過這個個人就比較不知道該怎麼用了?
而如果要取得特定型別的數值範圍的話,除了可以用 <limits> 的 std::numeric_limits<TYPE> 這提供的各種函式(參考)來確認之外,在 <cstdint> 中也根據不同的型別提供了巨集形式的最大值和最小值。
像是 int8_t 的值的範圍就是 INT16_MIN ~ INT16_MAX;而 uint_fast32_t 的範圍則是 0 ~ UINT_FAST32_MAX。
此外,在字元的部分,除了本來的 char 和 wchar_t 之外,現在也多出了 char8_t、char16_t、char32_t 這種對應 UTF-8、UTF-16、UTF-32 的字元型別(參考);不過在規範上,他們似乎不保證是 8 / 16 / 32 bit,而是類似 int_leastN_t 這樣,是要求可以處理對應的資料而已。
而對應的字串也多了對應的 std::u8string、std::u16string、std::u32string(參考);但是以 C++20 的標準來說,這些新的字串型別算是相容性極差、相當難以使用的。
而在 C++23 的標準,也針對了浮點數的部分,增加了 <stdfloat>、提供了類似的型別定義(參考、參考),這邊包括了:float16_t、float32_t、float64_t、float128_t、bfloat16_t 這些型別。
