【问题标题】:printf with multiple pointers as arguments in Cprintf在C中具有多个指针作为参数
【发布时间】:2020-11-10 04:34:35
【问题描述】:

当我们在没有格式说明符的情况下向它提供多个参数时,printf() 的行为是什么?

例子:

int main() 
{
    printf("hello", "hi");
    return 0;
}

为什么编译器在编译上述程序时会产生警告? :

warning: too many arguments for format [-Wformat-extra-args]

如果我们编译下面类似的程序:

int main() 
{
    char *s1 = "hello";
    char *s2 = "hi";
    printf(s1, s2);
}

不会产生任何警告。这是什么原因?

另外,为什么两个程序都只输出hello,而不输出hi

【问题讨论】:

  • 您错误地使用了printf。第一个参数应该是格式字符串。后续参数应在格式字符串中具有格式说明符。例如:printf("%s %s", s1, s2);。我认为char *s1 "hello %s"; char *s2 = "hi"; printf(s1, s2); 会起作用,但这是不好的做法。
  • 您的编译器在第一种情况下很有帮助(并且忽略了 C 的基本规则),它尊重基本规则 “程序员知道她在做什么”第二种情况。然而,在这些 sn-ps 中,程序员实际上并不知道她在做什么(或自愿做错事(为什么??????))
  • @atul_pant 请阅读函数 printf 的描述。这对你很有用。
  • 您可能认为printf 就像其他语言(Python、perl 等)中的打印函数,您可以给它许多字符串,它会打印所有字符串。它不是那样的。正如 Vlad 所说,您需要阅读它的文档,或者在线上存在的无数关于其使用的教程。
  • 形式上,第一个参数应该是格式字符串,后面的参数根据嵌入在格式字符串中的格式说明符进行解释(如%d,@ 987654332@等)。由于您的字符串 "hello" 不包含任何格式说明符,因此所有后续参数都将被忽略。

标签: c printf language-lawyer variadic-functions format-string


【解决方案1】:

许多编译器非常了解printf 函数族,并在编译时读取格式字符串分析参数。 printf("hello",s2); 编译器看到格式字符串中没有%... 并且不期望任何其他参数。发出警告

如果你调用printf(s1,s2);,编译器不知道s1的内容是什么,它不能通过格式字符串,也不会发出警告。

许多编译器都有特殊的扩展来通知他们你的函数是printf 喜欢并且你希望编译器读取格式字符串 - gcc:

      extern int
      my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));

【讨论】:

    【解决方案2】:

    tl;dr:printf() 的额外参数将被忽略。

    官方C language standard(链接是C11版本的草稿)是这样说的:

    § 7.21.6.1 fprintf 函数

    1. ...

    2. ...如果格式已用尽而参数仍然存在,则评估多余的参数(一如既往),否则将被忽略。 fprintf 函数在遇到格式字符串结尾时返回。

    ...而printf() 只是fprintf() 针对标准输出文件。

    关于你的两个代码sn-ps:

    • 对于第一个 sn-p,编译器会提示您参数的数量与格式字符串中的说明符数量不匹配。这只是一种礼貌 - 不需要注意这一点。这也解释了为什么编译器在第二个 sn-p 中没有注意到它。它可以,但是追逐你的指针并检查它们所指向的东西太费力了。

    • 在这两种情况下,格式字符串都是printf() 的第一个参数,即"hello"。该字符串没有格式说明符,因此printf() 查看"hello",并理解它只需要打印它并且不需要处理任何其他参数。这就是它忽略"hi"的原因。

    【讨论】:

      【解决方案3】:

      C 2018 标准在第 7.21.6.3 条中指定 printf 的行为,其中第 2 段说“printf 函数等效于 fprintf,参数 stdout 插入在 @987654325 的参数之前@。”

      标准在 7.21.6.1 中规定了fprintf 的行为,它告诉我们第二个参数(printf 的第一个参数)是一个格式字符串,它可能包含由字符“%”引入的各种转换规范”。因此,在printf("hello", "hi") 中,"hello" 是没有转换规范的格式字符串。在这种情况下,第 2 段告诉我们会发生什么:

      如果格式已用尽[完全处理]而参数仍然存在,则评估多余的参数(一如既往),否则将被忽略。

      因此,在printf("hello", "hi") 中,"hi" 被忽略,"hello" 是一个仅包含普通字符的格式字符串,这些字符在第 3 段中被复制到输出流中。

      编译器警告printf("hello", "hi"),因为它能够看到这个调用包含一个多余的参数,因为格式字符串不包含它的转换规范。

      您的编译器不会对printf(s1,s2); 发出警告,因为它不会分析s1 在此调用期间将包含的内容。这种分析在这种情况下并非不可能,但这样的情况很少见:当程序员使用指向字符串的指针作为printf 的格式字符串时,它通常是一个字符串或指针,它被计算、构造或在程序执行过程中选择,而这种计算的方式往往超出了编译器的分析能力。指针明显是指向固定字符串的指针的情况很少见,因为它们并不经常有用,因此可能编译器实现者没有发现实现编译器处理这些情况所需的代码是有价值的。

      【讨论】:

        【解决方案4】:

        printf的第一个参数是格式字符串,因为printf是关于打印格式化数据的。要指定如何格式化数据,printf 使用第一个参数。这与其他所有参数(如 Python 的 print)都以相同方式使用并通过其他方式完成格式化的语言和库不同。

        您提供的第一个和第二个示例都是“不正确的”,尽管在技术上是有效的,因为您传递的格式字符串不需要任何额外参数,因此 "hi" 未使用。

        您可能想要做的是:

        printf("%s %s", "hello", "hi");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-06-22
          • 1970-01-01
          • 1970-01-01
          • 2017-11-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多