2

我正在将 mac 地址的字符串表示形式转换为UINT8定义为 s的数组unsigned char。我很好奇为什么当我读入一个 ssscanf()数组时会读取全 0,UINT8而当我读入一个常规 32 位ints 数组时会读入实际值。它几乎就像是砍掉了 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);
}
4

1 回答 1

5

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


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

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. 根据标准,再次,

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

因此,提供错误的类型作为参数是调用 未定义的行为


解决方案:

为了表明您将提供一个(有符号或无符号)char类型参数,您需要使用以hh长度修饰符为前缀的格式说明符,就像%hhx家庭scanf()一样。

于 2015-08-04T16:57:54.017 回答