背景
今天使用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;