【C 語言】資料型別的一致性
背景
今天使用Visual Studio 2017 寫一個程式時發現的一個問題,做下記錄。
目標實現
定義 char 型別的陣列,將部分引數與巨集進行比較,輸出比較結果。
問題描述
問題程式碼
char addbuf[8] = {0x5A, 0xA5, 0x00, 0x01};
printf("-91's HEX: %x\r\n", -91);
// 列印 addbuf
for (int i = 0; i < 4; i++)
{
printf("addbuf[%d]'s HEX:%8x, DEC: %8d \r\n", i, addbuf[i], addbuf[i]);
}
if ( addbuf[1] == 0xA5)
{
printf("Done\r\n");
}
else
{
printf("Undone\r\n");
}
執行結果
分析
addbuf 陣列的型別為 char,即其元素的表示範圍為:
DEC:[ -128,127]
HEX:[0x00,0xFF]
對於 addbuf[1] 在初始化為 0xA5, 實際應用值為 -91。
- 異常點 1:在對 addbuf 進行十六進位制和十進位制列印時,發現在 HEX 列印時,addbuf[1] 的值為 0xffffffa5。
- 異常點 2:判斷 addbuf[1] 是否等於 0xA5 時,判斷結果為不等於,列印判斷結果為:Undone。
首先,addbuf[1] 初始化為 0xA5。對於 0xA5 常量,系統會預設為是 int 型別的 165,而不是 char 型別的 -91。也就是說,在 addbuf[1] 初始化時,進行了一次強制型別轉換。而對於此次的型別轉換實際上是溢位的。
其次,%x 列印物件型別是 unsigned int。也就是說,所有的型別在進行列印之前,都會被強制轉換為 unsigned int 型別再列印。所以,在對 addbuf[1] 進行 %x 列印時,出來的顯示值為:0xffffffa5。
再兒,由於常量 0xA5 是 int 型別的 165,當拿 char 型別的 addbuf[1] 與 0xA5 比較時,相當於是 -91 與 165 進行比較,比較結果為:不相等。因此列印資訊為:Undone。
解決方案
由分析可知,問題的根本原因是資料型別不一致導致的。只要將資料型別同一起來,就可以解決此問題。
方案 1
if 語句判斷時,將 0xA5 強制轉換型別為 char 型別。這不是個常規的方式,因為需要在每個比較運算的地方都需要加上強制型別轉換,對於程式碼編寫和維護,都不是個明智的選擇。
if (addbuf[1] == (char)0xA5)
方案 2
將 addbuf 定義為 unsigned char。此時能將資料型別統一為無符號型別,無論在比較或列印時,都不會出現異常了。
unsigned char addbuf[8] = { 0x5A, 0xA5, 0x00, 0x01 };
執行結果
方案 3
使用 #include "stdint.h"
的資料型別。stdint.h 重新對整型進行了封裝,從型別名稱就可以得知資料的有效範圍,對於不同的編譯環境均能適用,更建議使用此方式。
typedef signed char int8_t;
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int_least8_t;
typedef short int_least16_t;
typedef int int_least32_t;
typedef long long int_least64_t;
typedef unsigned char uint_least8_t;
typedef unsigned short uint_least16_t;
typedef unsigned int uint_least32_t;
typedef unsigned long long uint_least64_t;
typedef signed char int_fast8_t;
typedef int int_fast16_t;
typedef int int_fast32_t;
typedef long long int_fast64_t;
typedef unsigned char uint_fast8_t;
typedef unsigned int uint_fast16_t;
typedef unsigned int uint_fast32_t;
typedef unsigned long long uint_fast64_t;
typedef long long intmax_t;
typedef unsigned long long uintmax_t;