如果我们打印一个包含 "%s" 的字符串,输出会是什么?

What will be the output, if we print a string that contains "%s" in it?

我想知道如果我们打印一个内容中包含“%s”的字符串,结果会是什么?

我尝试将其打印为 "hi%s"。

char *ptr="hi%s";
printf(ptr);

我希望输出为 "hi"。 但我得到的是 "hi hi%s".

"hi hi%s"

会有未定义的行为。:)

字符串文字的这一部分 %s 被函数视为格式说明符。

来自C标准(7.21.6.1 fprintf函数)

  1. ... If there are insufficient arguments for the format, the behavior is undefined.

如果你想输出字符串 "hi%s" 你应该像 "hi%%s".

这样定义文字

这是一个演示程序

#include <stdio.h>

int main(void) 
{
    char *ptr = "hi%%s";

    printf( ptr );

    return 0;
}

它的输出是

hi%s

您的程序调用 undefined behaviour.

您的代码等同于

 printf("hi%s");

其中 %s 是一个转换说明符,它需要提供一个参数。

引用 C11,章节 §7.21.6.1

[...] If there are insufficient arguments for the format, the behavior is undefined. [....]

建议:如果只打印一个字符串,不需要任何转换(格式化),可以使用puts().

你不是 "printing a string which has %s in its content"。您正在将 格式字符串 之类的字符串传递给 printf,并且在格式字段没有匹配参数的情况下这样做,您的程序有 undefined行为printf 的第一个参数不是您要打印的字符串。它是一个格式字符串,用于控制剩余参数的形式 interpreted/converted,并且还可以包含要将它们合并到的文字文本。

"Printing a string which has %s in its content"(其中 ptr 指向该字符串,如您的问题所示)可以通过 printf("%s", ptr)puts(ptr).

完成

如其他回答中所述,将发生未定义的行为。

未定义行为在此上下文中的含义:printf 收到带有 n 个格式说明符(例如 %s)的字符串时,它将期望 n 个参数除了字符串之外还要传递给函数。因此,当您有这样的语句 printf("hi%s") 时,函数的行为就好像您传递了一个参数(在这种情况下,第二个参数应该是一个 char *),即使没有。该函数只是要获取堆栈中的下一个值,在这种情况下是一些垃圾值。然后该函数将尊重这个垃圾值并将其视为字符缓冲区。这种行为未定义的原因是不知道堆栈上的垃圾值可能是什么。

可能的结果

  1. 堆栈上的垃圾值是 0 -> 取消引用垃圾值时出现分段错误。

  2. 堆栈上的垃圾值恰好是一个有效的内存地址 -> 内存位置的字节将不断插入到字符串中(在这种情况下附加到 "hi")直到遇到值为 0 的字节或发生分段错误。

  3. 堆栈上的垃圾值不是 0 但不是有效的内存位置 -> 取消引用垃圾值时出现分段错误。

产生同样的情况 下面的代码部分与 printf("hi%s")

的情况非常相似
void foo() {
   char * myJunkValue;  // Since not a global/static variable, this is not guaranteed to be 0
   printf(myJunkValue); // Undefined behavior
}

正如其他回答所提到的,您正在执行的操作会调用 undefined behavior,因为您调用 printf 时没有为给定的格式说明符提供足够的参数。在你的情况下你得到了垃圾输出,但你的程序可能很容易崩溃。

要对此进行扩展,代码如下所示:

printf(ptr);

几乎总是错的。如果 ptr 指向的字符串可以被用户以任何方式控制,那么你就可以打开 format string vulnerability。用户可以使用 %s%x 等提供特制的字符串来转储内存内容并暴露敏感数据,或者使用 %n 的字符串可以写入内存并允许攻击者接管您的程序。

如果 printf 的第一个参数出于这个原因不是字符串常量,许多静态分析器会发出警告。