scanf%u负数?

时间:2016-07-31 13:20:56

标签: c printf scanf unsigned format-specifiers

我已经尝试了scanf("%u",&number)并输入了负数,问题是当我printf("%d",number)得到负数时。我以为这会阻止我阅读负数。 scanf("%d",&number)scanf("%u",&number)真的一样吗? 或者只是为了可读性。

我在做一些叫做未定义行为的事情吗?

编辑:

Wikipedia我读到了这个:

  

%u:扫描十进制无符号整数(注意在C99标准中   输入值减号是可选的,因此如果读取减号,则不   将出现错误,结果将是a的两个补码   负数,可能是一个非常大的值。

阅读SO答案及以上内容有点令人困惑。有人能说得更清楚吗?

2 个答案:

答案 0 :(得分:2)

是的,无论如何,这都是未定义的行为。

考虑变量number的类型为unsigned%d中的printf()期望参数为signed int类型,并传递unsigned类型是UB。

OTOH,如果number是签名类型,则使用%u进行扫描首先是UB。

正如您所期望的那样

  

[...]阻止我阅读负数

格式说明符阻止输入不正确。如果格式说明符与提供的参数不匹配,则会调用undefined behavior

引用C11,附件J.2,调用UB的场景,

  

其中一个格式化输入函数的转换结果不能   在相应的对象中表示,或者接收对象没有   适当的类型

答案 1 :(得分:1)

正如Sourav Ghosh详细解释的那样,使用与实际类型不一致的格式是一个潜在的问题。然而对于这种特殊情况,在当前的PC体系结构中,这不是一个真正的问题,因为intunsigned int都没有陷阱表示。

您可以使用scanf("%u", &number);扫描负数。它将在目标类型中被否定,即unsigned int,具有与带符号int中的负数相同的按位表示,用于二进制补码表示,这在当前体系结构上几乎是通用的。

  

scanf通过匹配可选的带符号十进制整数来转换%u,其格式与strtoul函数的主题序列的预期相同,值为10基本论点。相应的参数应该是指向无符号整数的指针。

     

strtolstrtollstrtoulstrtoull函数将nptr指向的字符串的初始部分转换为long intlong long intunsigned long intunsigned long long int表示。首先,它们将输入字符串分解为三个部分:初始的,可能为空的白色空格字符序列(由isspace函数指定),类似于由值确定的某个基数表示的整数的主题序列base,以及一个或多个无法识别的字符的最终字符串,包括输入字符串的终止空字符。然后,他们尝试将主题序列转换为整数,并返回结果。

     

如果base的值在2到36之间(包括),则主题序列的预期形式是一个字母和数字序列,表示一个整数,其基数由base指定,可选地前后加号或减号,但不包括整数后缀。

     

...如果主题序列具有预期形式且base的值在2到36之间,则将其用作转换的基础,将其值归于每个字母,如上所述。如果主题序列以减号开头,则转换产生的值将被否定(在返回类型中)。

如果number的类型为unsigned int,则定义行为并使用无符号否定语义解析负值并将其存储到number中。使用printf("%d", number);打印此值最多只是实现定义,但在当前的PC架构上,将打印最初由scanf("%u", &number);解析的负数

结论:虽然它似乎无害,但在intunsigned int中使用printfscanf并使用错误的格式非常草率。事实上,在表达式中混合有符号和无符号类型,尤其是在比较中非常容易出错,因为这种结构的C语义有时会违反直觉。