sscanf() 十六进制整数到整数数组与无符号字符
sscanf() hex ints into an array of ints vs. unsigned chars
我正在将 mac 地址的字符串表示形式转换为定义为 unsigned char
的 UINT8
数组。我很好奇为什么当我读入 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
长度修饰符为前缀的格式说明符,例如 %hhx
与 scanf()
个家庭。
我正在将 mac 地址的字符串表示形式转换为定义为 unsigned char
的 UINT8
数组。我很好奇为什么当我读入 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 thestrtoul()
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
长度修饰符为前缀的格式说明符,例如 %hhx
与 scanf()
个家庭。