sscanf() 十六进制整数到整数数组与无符号字符

sscanf() hex ints into an array of ints vs. unsigned chars

我正在将 mac 地址的字符串表示形式转换为定义为 unsigned charUINT8 数组。我很好奇为什么当我读入 UINT8 数组时 sscanf() 将读取全 0 而当我读入常规 32 位 int 数组时读取实际值。它几乎就像它正在砍掉 int 错误末端的 8 位。

char *strMAC = "11:22:33:AA:BB:CC";

typedef unsigned char UINT8;
UINT8 uMAC[6];

int iMAC[6];

sscanf( (const char*) strMac, 
        "%x:%x:%x:%x:%x:%x", 
        &uMAC[0], &uMAC[1], &uMAC[2], &uMAC[3], &uMAC[4], &uMAC[5] );
printf( "%x:%x:%x:%x:%x:%x", 
        uMAC[0], uMAC[1], uMAC[2], uMAC[3], uMAC[4], uMAC[5] );
// output: 0:0:0:0:0:0

sscanf( (const char*) strMac, 
        "%x:%x:%x:%x:%x:%x", 
        &iMAC[0], &iMAC[1], &iMAC[2], &iMAC[3], &iMAC[4], &iMAC[5] );
printf( "%x:%x:%x:%x:%x:%x", 
        iMAC[0], iMAC[1], iMAC[2], iMAC[3], iMAC[4], iMAC[5] );
// output: 11:22:33:AA:BB:CC

更新:%hhx 适用于 C99 及更高版本,但我有一个旧代码库,所以我最终选择了 strtoul():

char *str = strMac;
int i = 0;
for(i = 0; i < 6; i++, str+=3) {
    uMAC[i] = strtoul(str, NULL, 16);
}

TL;DR - 由于参数类型不匹配,第一个片段 invkoes UB。


详细说明,引用 %x 格式说明符的参数类型要求,来自 C11 标准,第 7.21.6.2 章,fscanf() 函数,(强调我的)

x Matches an optionally signed hexadecimal integer, whose format is the same as expected for the subject sequence of the strtoul() function with the value 16 for the base argument. The corresponding argument shall be a pointer to unsigned integer.

所以,在使用

 sscanf( (const char*) strMac, 
    "%x:%x:%x:%x:%x:%x", 
    &uMAC[0], &uMAC[1], &uMAC[2], &uMAC[3], &uMAC[4], &uMAC[5] );

在您的代码中,您为 %x 提供了错误类型的参数。根据标准,再次

[...]. Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

因此,提供错误的类型作为参数调用 undefined behaviour


解法:

要指示您要提供(有符号或无符号)char 类型参数,您需要使用以 hh 长度修饰符为前缀的格式说明符,例如 %hhxscanf() 个家庭。