這篇簡單來記錄一下 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
這些型別。