如果我们打印一个包含 "%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函数)
- ... 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 *),即使没有。该函数只是要获取堆栈中的下一个值,在这种情况下是一些垃圾值。然后该函数将尊重这个垃圾值并将其视为字符缓冲区。这种行为未定义的原因是不知道堆栈上的垃圾值可能是什么。
可能的结果
堆栈上的垃圾值是 0 -> 取消引用垃圾值时出现分段错误。
堆栈上的垃圾值恰好是一个有效的内存地址 -> 内存位置的字节将不断插入到字符串中(在这种情况下附加到 "hi")直到遇到值为 0 的字节或发生分段错误。
- 堆栈上的垃圾值不是 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
的第一个参数出于这个原因不是字符串常量,许多静态分析器会发出警告。
我想知道如果我们打印一个内容中包含“%s”的字符串,结果会是什么?
我尝试将其打印为 "hi%s"。
char *ptr="hi%s";
printf(ptr);
我希望输出为 "hi"。 但我得到的是 "hi hi%s".
"hi hi%s"
会有未定义的行为。:)
字符串文字的这一部分 %s
被函数视为格式说明符。
来自C标准(7.21.6.1 fprintf函数)
- ... 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 *),即使没有。该函数只是要获取堆栈中的下一个值,在这种情况下是一些垃圾值。然后该函数将尊重这个垃圾值并将其视为字符缓冲区。这种行为未定义的原因是不知道堆栈上的垃圾值可能是什么。
可能的结果
堆栈上的垃圾值是 0 -> 取消引用垃圾值时出现分段错误。
堆栈上的垃圾值恰好是一个有效的内存地址 -> 内存位置的字节将不断插入到字符串中(在这种情况下附加到 "hi")直到遇到值为 0 的字节或发生分段错误。
- 堆栈上的垃圾值不是 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
的第一个参数出于这个原因不是字符串常量,许多静态分析器会发出警告。