1. 程式人生 > >【C 語言】資料型別的一致性

【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;

在這裡插入圖片描述