跳轉到內容

C 程式設計/stdint.h

來自華夏公益教科書,自由的教科書

stdint.h標頭檔案C 標準庫 中引入 C99 標準庫 第 7.18 節,以允許程式設計師透過提供一組型別定義來編寫更可移植的程式碼,這些型別定義指定了固定寬度的整型,以及使用宏定義的每個型別的允許的最小值和最大值[1]。此標頭檔案對於嵌入式程式設計特別有用,嵌入式程式設計通常涉及對硬體特定 I/O 暫存器的相當多的操作,這些暫存器需要固定寬度的整數資料、特定位置和精確的對齊方式。stdint.h(用於 C 或 C++)cstdint(用於 C++) 可以下載或快速建立,如果它們沒有提供。

固定寬度整型的命名約定是 intN_t 用於 帶符號的 intuintN_t 用於 無符號的 int [1]。例如,int8_tuint64_t 以及其他一些可以宣告,並定義其相應的範圍 INT8_MININT8_MAX0(零)到 UINT64_MAX;同樣使用類似的但大寫的命名約定。此外,stdint.h 定義了能夠儲存物件指標的整型限制,例如 UINTPTR_MAX,其值取決於處理器及其地址範圍[1]

固定寬度型別及其相應的範圍僅在特定編譯器/處理器存在時才包含在該標頭檔案中。請注意,即使在同一個處理器上,兩個不同的編譯器實現也可能不同。使用 #if#ifdef 允許透過使用編譯器的預處理器來包含或排除型別,以便為編譯器及其處理器目標選擇正確的固定寬度集。

相關的包含檔案 <limits.h> 提供了用於常見整型變數範圍限制的宏值。在 C 中,<limits.h> 已經包含在 <stdint.h> 中,但與 <stdint.h> 不同,它是實現獨立的;<limits.h> 中定義的所有最大和最小整數值都是編譯器實現特定的。例如,生成 32 位可執行檔案的編譯器將定義 LONG_MIN 為 −2,147,483,648 [−231],但是對於 64 位處理器目標,LONG_MIN 可以是 −9,223,372,036,854,775,808 [−263]。

對應的整型

[編輯 | 編輯原始碼]

C 標準有“對應的整型”的概念。非正式地說,這意味著對於任何整型 T

typedef signed T A;
typedef unsigned T B;

型別 A 和型別 B 被稱為 對應的整型(注意:typedef 不會建立新的型別,它建立新的識別符號作為給定型別的同義詞)。這很重要有兩個原因

  • 對應型別對別名和型別雙關語友好
  • 對應型別具有類似的物件表示

這兩者結合起來需要像

A a = 1;
B b = 1;
*(B*)&a = 0;
*(A*)&b = 0;

這樣的程式碼透過標準具有定義的行為(而不是在一般情況下未定義)。關於你可以將此推到多遠有很多注意事項,因此務必實際閱讀 C 標準以瞭解什麼合法,什麼不合法(這部分內容主要涉及填充位和超出範圍表示)。

C99 標準闡述了值表示和物件表示之間的區別。

整型的物件表示由 0 個或多個填充位、1 個或多個值位[1] 以及取決於整型符號的 0 個或 1 個符號位(這不計為值位)組成。

值表示是整型的概念表示。值表示忽略任何填充位,並對位進行(可能的)重新排列,以便整型按順序從最高有效值位到最低有效值位排序。大多數程式設計師處理這種表示,因為它允許他們透過只處理 -0 和超出範圍的值來輕鬆編寫可移植程式碼,而不是在處理物件表示時,除了這兩個值之外,還要處理棘手的別名規則和陷阱表示。

有符號表示

[編輯 | 編輯原始碼]

C 標準只允許編譯器編寫者指定三種有符號整數表示

  • 符號和大小
  • 反碼
  • 補碼(最廣泛使用)

型別 <something>_tu<something>_t 必須是對應的帶符號和無符號整型。對於標記為可選的型別,實現必須同時定義 <something>_tu<something>_t,或者兩者都不定義。這些型別的限制應使用與下面描述的類似方式的宏來定義。

如果型別為 [u]<something>N_t(或類似於預處理程式定義),N 必須是正十進位制整數,沒有前導 0。

固定寬度整型

[編輯 | 編輯原始碼]

這些型別為 intN_tuintN_t。兩種型別都必須由正好 N 位表示,沒有填充位。intN_t 必須編碼為補碼帶符號整數,uintN_t 編碼為無符號整數。這些型別是可選的,除非實現支援寬度為 8、16、32 或 64 的型別,那麼它應將它們 typedef 為具有對應 N 的對應型別。任何其他 N 都是可選的[1]

特定整型型別限制
說明符 簽名 位元組 最小值 最大值
int8_t 有符號 8 1 −27 等於 −128 27 − 1 等於 127
uint8_t 無符號 8 1 0 28 − 1 等於 255
int16_t 有符號 16 2 −215 等於 −32,768 215 − 1 等於 32,767
uint16_t 無符號 16 2 0 216 − 1 等於 65,535
int32_t 有符號 32 4 −231 等於 −2,147,483,648 231 − 1 等於 2,147,483,647
uint32_t 無符號 32 4 0 232 − 1 等於 4,294,967,295
int64_t 有符號 64 8 −263 等於 −9,223,372,036,854,775,808 263 − 1 等於 9,223,372,036,854,775,807
uint64_t 無符號 64 8 0 264 − 1 等於 18,446,744,073,709,551,615

這些型別的限制使用以下格式的宏定義:

  • INTN_MAX 是 intN_t 的有符號版本的最大值 (2N−1 − 1) 。
  • INTN_MIN 是 intN_t 的有符號版本的最小值 (−2N−1) 。
  • UINTN_MAX 是 uintN_t 的無符號版本的最大值 (2N – 1) 。

最小寬度整數型別

[編輯 | 編輯原始碼]

它們採用 int_leastN_tuint_leastN_t 的形式。int_leastN_t 是有符號整數,uint_leastN_t 是無符號整數[1]

標準要求這些型別的寬度大於或等於 N,並且沒有更小的型別具有相同的符號並且具有 N 或更多位。例如,如果一個系統只提供 uint32_tuint64_tuint_least16_t 必須等價於 uint32_t

實現需要為以下 N 定義這些型別:8、16、32、64。其他任何 N 都是可選的。

這些型別的限制使用以下格式的宏定義:

  • INT_LEASTN_MAXint_leastN_t 的有符號版本的最大值 (2N−1 − 1 或更大) 。
  • INT_LEASTN_MINint_leastN_t 的有符號版本的最小值 (−2N−1 + 1 或更小) 。
  • UINT_LEASTN_MAXuint_leastN_t 的無符號版本的最大值 (2N − 1 或更大)。

stdint.h 還應該定義宏,這些宏將轉換常量十進位制、八進位制或十六進位制值,這些值保證適合相應的型別並且可以使用 #if

  • INTN_C(value) 被替換為適合 int_leastN_t 的值。例如,如果 int_least64_t 被“typedef”為 signed long long intINT64_C(123) 對應於 123LL
  • UINTN_C(value) 被替換為適合 uint_leastN_t 的值。

最快的最小寬度整數型別

[編輯 | 編輯原始碼]

它們採用 int_fastN_tuint_fastN_t 的形式。

標準沒有對這些型別進行任何強制要求,除了它們的寬度必須大於或等於 N。它還留給實現者決定“快速”整數型別意味著什麼。

實現需要為以下 N 定義這些型別:8、16、32、64[2]

這些型別的限制使用以下格式的宏定義:

  • INT_FASTN_MAXint_fastN_t 的有符號版本的最大值 (2N−1 − 1 或更大) 。
  • INT_FASTN_MINint_fastN_t 的有符號版本的最小值 (−2N−1 + 1 或更小) 。
  • UINT_FASTN_MAXuint_fastN_t 的無符號版本的最大值 (2N − 1 或更大)[1]

足夠大以容納指標的整數

[編輯 | 編輯原始碼]

intptr_tuintptr_t 分別是有符號和無符號整數,它們保證能夠容納指標的值。這兩種型別都是可選的。

這些型別的限制使用以下宏定義:

  • INTPTR_MINintptr_t 的最小值 (−32,767 [−215 + 1] 或更小) 。
  • INTPTR_MAXintptr_t 的最大值 (32,767 [215 − 1] 或更大) 。
  • UINTPTR_MAXuintptr_t 的最大值 (65,535 [216 − 1] 或更大)[3]

最大寬度整數型別

[編輯 | 編輯原始碼]

intmax_tuintmax_t 是有符號和無符號整數,它們具有最大的支援寬度。換句話說,它們是具有最大限制的整數型別。

這些型別的限制使用以下格式的宏定義:

  • INTMAX_MAXintmax_t 的有符號版本的最大值 (9,223,372,036,854,775,807 [263 − 1] 或更大) 。
  • INTMAX_MINintmax_t 的有符號版本的最小值 (−9,223,372,036,854,775,807 [−263 + 1] 或更小) 。
  • UINTMAX_MAXuintmax_t 的無符號版本的最大值 (18,446,744,073,709,551,615 [264 − 1] 或更大) 。

還定義了宏,這些宏將轉換常量十進位制、八進位制或十六進位制值,這些值將適合相應的型別。

  • INTMAX_C(value) 被替換為適合 intmax_t 的值。
  • UINTMAX_C(value) 被替換為適合 uintmax_t 的值[1]

其他整數限制

[編輯 | 編輯原始碼]
  • PTRDIFF_MINptrdiff_t 的最小值。
  • PTRDIFF_MAXptrdiff_t 的最大值。
  • SIZE_MAXsize_t 的最大值 (216 − 1 或更大) 。
  • WCHAR_MINwchar_t 的最小值。
  • WCHAR_MAXwchar_t 的最大值。
  • WINT_MINwint_t 的最小值。
  • WINT_MAXwint_t 的最大值。
  • SIG_ATOMIC_MINsig_atomic_t 的最小值。
  • SIG_ATOMIC_MAXsig_atomic_t 的最大值。

批評和注意事項

[編輯 | 編輯原始碼]
  • 一些(不符合標準的)實現將 C99 支援附加到 C89 執行時庫的頂部。[需要引用] 這樣做的後果之一是,新的 printfscanf 說明符不被識別,並且可能會導致一些未定義的行為。解決此問題的典型方法是
    • 最常見(也是最錯誤)的方法是使用 longunsigned long 型別作為中間步驟,並將這些型別傳遞給 printfscanf。這對於小於 32 位的精確、最小和快速整數型別來說效果很好,但可能會對 ptrdiff_tsize_t 以及大於 32 位的型別造成問題,通常在使用 32 位 long 和 64 位指標的平臺上。
    • 不直接使用 scanf,而是手動讀取緩衝區,呼叫 strto<i|u>max,然後將其轉換為所需的型別。但這對列印整數沒有幫助。
    • 使用與 C99 相容的第三方 printfscanf 庫。
    • 使用 C99 標準列印格式說明符。例如,PRId64。這些在 inttypes.h 中宣告。
  • 整數等級和相應整數型別的規則可能會迫使實現者在不支援整數型別、做出糟糕的妥協或以不符合標準的方式支援整數型別之間選擇兩害相權取其輕。
    • 例如,有一些機器要麼對極其大的有符號整數暫存器或極其大的無符號整數暫存器有特殊支援,而對另一種型別卻沒有支援。[需要引用] 實現可以選擇不將其公開給 C 實現,合成一個緩慢的型別作為相應的整數型別,合成一個奇怪的相應整數型別,或者將整數公開給程式設計師而不將其設定為 [u]intmax_t 型別或合成一個相應的整數型別。
  • [u]intN_t 型別是對希望獲得保證的二進位制補碼整數型別和希望獲得保證的沒有填充位的型別的折衷方案(與更細粒度的方案相比,更細粒度的方案將定義更多型別)。由於對 [u]intN_t 型別採取“全有或全無”的方法,實現可能不得不玩上面描述的同類遊戲,具體取決於他們是否關心速度、程式設計師便利性或標準一致性。

說明和參考資料

[編輯 | 編輯原始碼]
  1. a b c d e f g h 從技術上講,它實際上允許 0 個或多個值位,但唯一可以構造它的方法是用有符號整數的單位元位域
  2. http://www.tuxgraphics.org/common/src2/article09043/avr-libc-user-manual-1.6.4/group__avr__stdint.html
  3. http://linux.die.net/man/3/intptr_t
  • stdint.h: 整數型別 – 基礎定義參考,The Single UNIX® Specification,來自 The Open Group 的第 7 版
[編輯 | 編輯原始碼]

由於 stdint.h 沒有與舊的 C++ 編譯器和 Visual Studio C++ 產品(Visual Studio 2010 之前的版本)一起提供,因此第三方實現可用。

  • pstdint.h – 來自 Paul Hsieh 的跨平臺免費實現。此實現已在以下編譯器上測試,所有編譯器在各自的最高設定下均無警告:Borland Turbo C 2.0、WATCOM C/C++ 11.0(16 位和 32 位)、Microsoft Visual C++ 6.0(32 位)、Microsoft Visual Studio.net(VC7)、Intel C++ 4.0、GNU gcc v3.3.3。
  • msinttypes – 符合 ISO C9x 的 stdint.hinttypes.h 可用於 Microsoft Visual Studio,在 Google Code 上提供。此實現已在 Microsoft Visual Studio 6.0、Microsoft Visual Studio .NET 2003、Microsoft Visual Studio 2005 和 Microsoft Visual Studio 2008 上測試。
  • boost/cstdint.hpp – 此檔案來自 Boost 庫,包含 stdint.h 中的資料型別。使用 <boost/cstdint.hpp> 的優點是它可以在許多平臺上使用。程式碼自然便攜,只要可以使用 boost 庫,就可以在任何平臺上編譯,無需修改。
華夏公益教科書