【问题标题】:How printf() function knows the type of its argumentsprintf() 函数如何知道其参数的类型
【发布时间】:2019-01-03 03:00:42
【问题描述】:

考虑以下程序,

#include <stdio.h>
int main()
{
    char a = 130;
    unsigned char b = 130;

    printf("a = %d\nb = %d\n",a,b);
    return 0;
}

此程序将显示以下输出。

a = -126
b = 130

我的问题是printf() 函数如何知道a 的类型是有符号的,b 的类型是无符号的以显示上述结果?

【问题讨论】:

  • char 可能在 [-128, 127] 范围内有效,因此 130 溢出。您的编译器会针对此类问题向您发出警告。
  • printf 函数不知道你传递了什么。它相信你告诉它的格式。只有聪明的编译器才能发现错误。
  • char 可以是 unsignedsigned 并且它纯粹是特定于实现的,在你的情况下虽然是 signed 这就是为什么当你打印超过 127 时它会溢出
  • printf 不知道,它相信格式说明符%u%d
  • @FelixPalmen 不会整数提升将unsigned char 转换为unsigned int?我认为整数促销会保留标志。

标签: c gcc printf


【解决方案1】:

printf() 知道类型,这就是为什么你必须给出正确的格式字符串。 printf() 的原型如下所示:

int printf(const char * restrict format, ...);

因此,唯一具有已知类型的参数是第一个参数,即格式字符串。

这也意味着在此之后传递的任何参数都受默认参数提升的影响——非常简化,阅读它,因为任何整数都将被转换为至少@987654324 @ -- 或者向谷歌询问该术语以了解每一个细节;)

在您的示例中,您有 实现定义的行为

char a = 130;

如果您的char 可以代表130,这就是您将在printf() 的输出中看到的内容。将值提升为 int 不会更改值。你得到的是一个负数,这意味着130 溢出了你的char。在 C 中转换期间溢出 signed 整数类型的结果是 implementation defined,您得到的值可能意味着在您的机器上,char 有 8 位(所以最大值是127) 并且有符号整数溢出导致环绕到负值范围。你不能依赖这种行为!

简而言之,负数是在这一行中创建的——130 的类型是int,将其分配给char 会对其进行转换,并且此转换会溢出。

一旦您的char 具有值-126,将其传递给printf() 只会将其转换为int,而不是更改值。

【讨论】:

    【解决方案2】:

    printf() 的附加参数根据类型说明符进行格式化。有关 C 格式说明符的列表,请参见此处。

    https://fr.cppreference.com/w/c/io/fprintf

    确实,由于您使用了%d 说明符而不是%u,因此在您的示例中不会期望b 被打印为130。这种令人惊讶的行为似乎在这里得到了解释。

    Format specifier for unsigned char

    我希望我能很好地回答你的问题。

    编辑:由于我的声誉低下,我无法评论 Felix Palmen 的回答。 默认参数提升确实似乎是这里的关键,但对我来说,除了a 的溢出之外,这里真正的问题是为什么b 仍然打印为130,尽管使用了签名说明符。它也可以用默认参数提升来解释,但应该更准确。

    【讨论】:

      【解决方案3】:

      你需要看一下stdio.hprintf语句的定义。您已经在评论printf 中得到了答案,只需将 format 指向的字符串写入标准输出即可。

      它是可变参数函数,它使用 vargas 来获取可变长度参数列表中的所有参数。

      你 这是来自glibc from the GNU version

      int __printf (const char *format, ...)
      {
         va_list arg;
         int done;
      
         va_start (arg, format);
         done = vfprintf (stdout, format, arg);
         va_end (arg);
      
         return done;
      }
      

      vfprintf 有什么作用?

      它只是将 format 指向的字符串写入流,以与 printf 相同的方式替换任何格式说明符,但使用 arg 标识的变量参数列表中的元素而不是附加的函数参数。

      更多关于vfprintf的信息

      【讨论】:

        【解决方案4】:

        printf() 不知道参数的数据类型。它适用于您传递的格式说明符。您使用的数据类型是char(范围从-128 到+127)和unsigned char(范围从0 到255)。 a 的输出在 127 之后溢出。因此输出为 -126。

        【讨论】:

        • char 有 8 位大小。 130如何导致8位溢出
        • @AjithcNarayanan char 没有有 8 位,但它很常见。您忘记了在有符号类型中需要 符号位。有关更多解释,请参阅我的答案。
        • 在 char 中存储 130 会设置符号位,但不会导致任何溢出,编译器也不会显示任何警告。请检查一下
        • @AjithcNarayanan 如果您使用 gcc,请尝试 -Wconversion。溢出不是这种转换标准的措辞,但是实现定义的事情确实会发生,因为130 不能在(8 位)signed char 中表示。
        猜你喜欢
        • 2011-07-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多