sscanf()十六进制为一组ints与unsigned chars

时间:2015-08-04 16:49:18

标签: c types scanf strtol

我正在将mac地址的字符串表示转换为UINT8的数组,定义为unsigned char。我很好奇当我读入常规32位sscanf()的数组时,当我读入UINT8 s数组和实际值时,int将读取所有0。它几乎就像是砍掉了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);
}

1 个答案:

答案 0 :(得分:5)

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

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

  

x匹配一个可选的带符号十六进制整数,其格式与strtoul()函数的主题序列的预期相同,其基值参数值为16。 相应的参数应该是指向无符号整数的指针。

所以,在使用

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

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

  

[...]。除非*表示分配抑制,否则   转换结果放在由尚未收到转换结果的format参数后面的第一个参数指向的对象中。如果此对象没有适当的类型,或者无法在对象中表示转换结果,则行为未定义。

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

<强>解决方案:

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