sscanf 处理最大无符号整数值的方式与赋值不同
sscanf handles maximal unsigned integer value differently than assignment does
考虑以下代码:
main()
{
int assigned = 4294967295; // Max unsigned integer value on 32-bits arch
char input[] = "4294967295";
int sscanned;
unsigned int result = sscanf(input, "%d", &sscanned);
printf ("scanned %u elements : %d\n
"Assigned j = %d\n",
result, sscanned, assigned);
return 0;
}
当为 32 位 arch 编译时(使用编译命令:gcc -Wall -Wextra -std=c11 -pedantic -m32 test_sscanf.c -o test_sscanf32
),它会发出有关“从‘long long int’到‘int’的转换溢出的预期警告将值从“4294967295”更改为“-1”[-Woverflow]”。
现在看到结果:
> ./test_sscanf32
scanned 1 elements : 2147483647
Assigned j = -1
虽然assigned
值已经正确地转换为最大负符号整数值,通过二进制补码表示(-1 = -2^31 + 2^ 30 + ... + 2^0),另一方面,scanned
值显然已经取消了它的 MSB,这导致它缩小到值 2147483647 = 2^31 - 1。
所以我的问题是:在n位机器[=]上最大n位整数值的处理方式有何不同? 34=](知道在 64 位架构上,会发生相同的行为)?
在给定的体系结构上,程序员是否没有权利期望 sscanf
以与赋值相同的方式处理值?
对 scanf
使用错误的格式说明符会调用 undefined behavior,因此结果没有意义。
例如,运行我机器上的相同代码给出以下结果:
scanned 1 elements : -1
Assigned j = -1
所以使用 %u
而不是 %d
。
通过强制转换或赋值将整数值转换为 int
,当该值不能由 int
表示但可以由具有更大范围的某些受支持类型表示时,会生成一个实现定义的值在 int
(C11 §6.3.1.3) 中。现在几乎所有的实现都定义了这种转换,使得 int x = UINT_MAX;
将 x 设置为 −1。我知道的唯一例外是 Unisys(née Burroughs)大型机,它仍然对负数使用补码表示法。
相比之下,所有 scanf
函数在读取超出变量类型可表示范围的数字时都具有 undefined 行为被写入 (C11 §7.21.6.2p10)。这意味着,您不仅 not 可以指望它做与整数转换相同的事情,您不能指望它做任何有建设性的事情,编译器会事实上有权生成机器代码,让恶魔从你的鼻子里飞出来。
我认为 7.21.6.2p10 是标准中的缺陷,但由于我认为 scanf
系列不适合 无论如何(这是只是他们的许多问题之一),我懒得提交 DR。请改用 strto*
函数。它们具有明确定义和记录的溢出行为。
考虑以下代码:
main()
{
int assigned = 4294967295; // Max unsigned integer value on 32-bits arch
char input[] = "4294967295";
int sscanned;
unsigned int result = sscanf(input, "%d", &sscanned);
printf ("scanned %u elements : %d\n
"Assigned j = %d\n",
result, sscanned, assigned);
return 0;
}
当为 32 位 arch 编译时(使用编译命令:gcc -Wall -Wextra -std=c11 -pedantic -m32 test_sscanf.c -o test_sscanf32
),它会发出有关“从‘long long int’到‘int’的转换溢出的预期警告将值从“4294967295”更改为“-1”[-Woverflow]”。
现在看到结果:
> ./test_sscanf32
scanned 1 elements : 2147483647
Assigned j = -1
虽然assigned
值已经正确地转换为最大负符号整数值,通过二进制补码表示(-1 = -2^31 + 2^ 30 + ... + 2^0),另一方面,scanned
值显然已经取消了它的 MSB,这导致它缩小到值 2147483647 = 2^31 - 1。
所以我的问题是:在n位机器[=]上最大n位整数值的处理方式有何不同? 34=](知道在 64 位架构上,会发生相同的行为)?
在给定的体系结构上,程序员是否没有权利期望 sscanf
以与赋值相同的方式处理值?
对 scanf
使用错误的格式说明符会调用 undefined behavior,因此结果没有意义。
例如,运行我机器上的相同代码给出以下结果:
scanned 1 elements : -1
Assigned j = -1
所以使用 %u
而不是 %d
。
通过强制转换或赋值将整数值转换为 int
,当该值不能由 int
表示但可以由具有更大范围的某些受支持类型表示时,会生成一个实现定义的值在 int
(C11 §6.3.1.3) 中。现在几乎所有的实现都定义了这种转换,使得 int x = UINT_MAX;
将 x 设置为 −1。我知道的唯一例外是 Unisys(née Burroughs)大型机,它仍然对负数使用补码表示法。
相比之下,所有 scanf
函数在读取超出变量类型可表示范围的数字时都具有 undefined 行为被写入 (C11 §7.21.6.2p10)。这意味着,您不仅 not 可以指望它做与整数转换相同的事情,您不能指望它做任何有建设性的事情,编译器会事实上有权生成机器代码,让恶魔从你的鼻子里飞出来。
我认为 7.21.6.2p10 是标准中的缺陷,但由于我认为 scanf
系列不适合 无论如何(这是只是他们的许多问题之一),我懒得提交 DR。请改用 strto*
函数。它们具有明确定义和记录的溢出行为。