【问题标题】:Why do the conversion specifiers, %o and %x, work differently for printf() and scanf() in C?为什么转换说明符 %o 和 %x 在 C 中对 printf() 和 scanf() 的工作方式不同?
【发布时间】:2015-10-19 14:00:44
【问题描述】:

我正在从 Stephen Prata 的“C Primer Plus”一书中学习 C。在第 4 章中,作者指出在printf() 中,%o 和 %x 分别表示 unsigned 八进制整数和 unsigned 十六进制整数,但在scanf() 中,% o 和 %x,分别解释 signed 八进制整数和 signed 十六进制整数。为什么会这样?

我在VS 2015中写了如下程序来检查作者的说法:

#include <stdio.h>
int main(void)
#pragma warning(disable : 4996)
{
   int a, b, c;

   printf("Enter number: ");
   scanf("%x %x", &a, &b);
   c = a + b;
   printf("Answer = %x\n", c);


   while (getchar() != EOF)
      getchar();
   return 0;
}

代码证明了作者的说法。

如果输入有一对整数,其中正整数的绝对值大于负整数的绝对值,那么一切正常。

但是,如果输入有一对整数,其中正整数的绝对值小于负整数的绝对值,那么输出就是您对 unsigned 的期望 2 的补码。

例如:

Enter number: -5 6
Answer = 1

Enter number: -6 5
Answer = ffffffff

【问题讨论】:

    标签: c io


    【解决方案1】:

    C 标准规定对于类似 printf 的函数 (7.21.6.1 fprintf):

    o,u,x,X
    unsigned int 参数被转换为无符号八进制 (o)、无符号十进制 (u) 或无符号十六进制表示法(x 或 X)

    对于类似 scanf 的函数,它说 (7.21.6.2 fscanf):

    x
    匹配可选带符号的十六进制整数,其格式相同 正如预期的 strtoul 函数的主题序列的值 16 为基本参数。对应的参数应该是一个指向 无符号整数。

    所以作为一个额外的功能,你可以写一个负的十六进制数,scanf 会将其转换为系统格式(二进制补码)中对应的无符号数。

    例如

    unsigned int x;
    scanf("%x", &x); // enter -1
    printf("%x", x); // will print ffffffff
    

    为什么他们觉得 scanf 需要这个稍微有用的功能,我不知道。也许它是为了与其他转换说明符保持一致。

    但是,这本书似乎错误地使用了该函数,因为标准明确规定您必须将指针传递给 unsigned int。如果你传递一个指向有符号整数的指针,你就正式调用了未定义的行为。

    【讨论】:

    • 格式化这种文本有多难,你不觉得麻烦吗?
    • @iharob 是的,所以引号对用户非常不友好 :) 一个秘诀是在引号内的一行后添加两个空格以换行。
    • 不,我知道,但我希望它看起来与 PDF 中的完全一样。查看我的答案,这是我找到的唯一解决方案。
    • @iharob &lt;pre&gt; html 标签似乎可以解决问题,但你不会得到花哨的黄色背景。
    • @Lundin:我通常使用 &lt;blockquote&gt; 标签,然后根据需要在块引用中使用其他 HTML 标签,例如 &lt;strong&gt;&lt;em&gt;&lt;pre&gt;&lt;code&gt;。 PITA,但它有效。
    【解决方案2】:

    阅读 C11 规范,第 7.21.6.2/12 节,它说o 格式:

    匹配一个可选带符号的八进制整数,其格式与 strtoul 函数的主题序列期望值为 8 对于base 参数。对应的参数应该是一个指向 无符号整数。

    带有对应的十六进制x格式文本。

    所以一方面规范说输入可以被签名,但它也说格式与读取无符号整数的strtoul函数相同,结果存储在一个无符号整数中。

    【讨论】:

    • 我从我的示例程序中看到scanf() 确实可以接收八进制和十六进制整数的有符号值。请您在您的系统中运行我的程序并像我一样使用输入吗?
    • @ShabbirKhan:重点是,你必须使用unsigned int 参数(scanf 的指针),而不是签名的int。接受前面的减号确实有点……出乎意料。
    • @Olaf,感谢您提供的信息!我会记住的。
    • strtoul 读取无符号整数的函数”不完整。 strtoul() 读取 unsigned and signed 数字的文本表示。结果是unsigned long
    【解决方案3】:

    确实正如@Joachim Pileborg指出的那样,作者是错误的

    这就是标准所说的

    7.21.6.2 fscanf 函数1

    12 转换说明符及其含义为:

    o       匹配一个可选有符号八进制整数,其格式与
             期望值为 8 的 strtoul 函数的主题序列
             用于基本参数。对应的参数应该是一个指向
             无符号整数。

    x       匹配一个可选有符号的十六进制整数,其格式与
             与 strtoul 函数的主题序列的格式相同,
                                                                                                 则
                               则
    相应的参数
             应为 指向无符号整数的指针。

    正如您在上面看到的那样,它是 可选 有符号的,但它当然需要一个指向和 无符号 整数的指针


    1当然我省略了很多,其实fscanf()是标准文档中最大的部分之一。

    【讨论】:

      猜你喜欢
      • 2018-03-30
      • 2012-06-22
      • 2010-12-25
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      • 1970-01-01
      • 2021-09-16
      • 1970-01-01
      相关资源
      最近更新 更多