考虑以下代码:
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位整数值的这种区别的理由是什么(知道64位arch,也会发生相同的行为)?
在给定的体系结构上,程序员是否无权正确地期望sscanf
会像赋值一样对待值?
答案 0 :(得分:3)
通过强制转换或赋值将整数值转换为int
时,如果该值不能用int
表示,但可以用某个较大范围的受支持类型来表示,则会在实现中定义实现值int
(C11 §6.3.1.3)。如今,几乎所有的实现都定义了这种转换,使得int x = UINT_MAX;
将x设置为-1。我知道的唯一例外是Unisys(néeBurroughs)大型机,该大型机仍对负数使用一补码表示法。
相比之下,所有scanf
函数在读取的数字超出该数字将被写入的变量类型的可表示范围之外时,均具有 undefined 行为({ {3}})。这意味着,您不仅可以不依靠它来执行与整数转换相同的操作,还不能依靠它来做任何有建设性的事情,并且实际上编译器将有权使用生成使恶魔从你的鼻子飞出来的机器代码。
我认为7.21.6.2p10是标准中的缺陷,但是由于我认为scanf
家庭仍然不符合目的 (这只是许多问题之一)和他们一起),我不会为提交DR感到烦恼。请改用strto*
函数。它们具有明确定义并记录的溢出行为。
答案 1 :(得分:2)
为scanf
使用错误的格式说明符会调用undefined behavior,因此结果不必说得通。
例如,在我的计算机上运行相同的代码将得到以下结果:
scanned 1 elements : -1
Assigned j = -1
因此,请使用%u
而不是%d
。