printf(string)与printf("%s", string)有什么不同?

5
我正在编写一个代理服务器,并遇到了一个奇怪的bug,希望有人能解释一下。
我正在接收来自客户端的GET请求的第一行。例如,客户端会发送以下请求:
GET http://en.wikipedia.org/wiki/Special:Random HTTP/1.0
Host: en.wikipedia.org
...

我会将这个请求转发给服务器。
然而,对于某些网址,我会遇到问题:
GET http://map.media6degrees.com/orbserv/curl=http%3A%2F%2Fwww.masteringemacs.org%2Farticles[trunc] HTTP/1.0

我将这行文本读入 char buffer[MAXLINE_LENGTH] 中,该缓冲区足够长以容纳该字符串。
当我打印接收到的 GET 请求时,
printf(buffer);

打印的字符串是:
GET http://map.media6degrees.com/orbserv/hbpix?pixId=2869&curl=http0X0.0000000000015P-10220.0000000.000000www.masteringemacs.org0.000000articles0.00000020100.000000110.000000010.000000running-shells-in-emacs-overview204741995430849962482228271154502456423284733956118041206315879167624419264810411254941012469231829496710329852458403099883653794777355548418601638730167027236864.000000 HTTP/1.0

这段文字的意思是:似乎%3A、%2F等字符已经被字符串格式化了。当我运行printf("%s",buffer);时,输出结果正确且符合预期。我理解为什么会发生这种情况; 我想知道为什么会以这种方式发生。printf“字符串格式化”的值是否来自堆栈上的任意区域? %3A 等是否是有效的格式化字符串?

请查看数据中的“%”符号... - dmckee --- ex-moderator kitten
4个回答

8

1)如果你看一下函数原型,你会发现 printf() 函数需要一个格式化字符串和零个或多个参数。所以严格来说,“printf(string)”是不正确的:

SYNOPSIS
       #include <stdio.h>

       int printf(const char *format, ...);
       int fprintf(FILE *stream, const char *format, ...);
       int sprintf(char *str, const char *format, ...);
       int snprintf(char *str, size_t size, const  char  *format,
       ...);

2) 第一个参数 将会 被解释为格式字符串,任何 "%XXX" 的出现都将被解释为占位符,而这恰好是正在发生的事情 :)

3) 当然,解决方案是使用 printf ("%s", string)

4) 或者使用 puts(string) 代替 :)


@yzb3 - 你说得完全正确 :) 我已经相应地编辑了我的回复 :) - paulsm4
puts(string)并不完全相同,因为它会附加一个换行符。但是fputs(string, stdout)应该可以工作。 - Joshua Green
好的,这就是我想到的。那么,在这种情况下,其他值从哪里来? - Eagle
Joshua Green - 重申一遍:你是绝对正确的。@Eagle - 噢!!!!!严格来说,你不应该只有一个字符串参数。如果你这样做了,最好不要在其中使用“%”符号。但是如果你这样做了,那么gcc会很好地替换为“fputs()”。这是编译器特定的。你不能依赖它。显然,在你的编译器中不起作用 :) - paulsm4
Printf是一个可变参数函数,它接受一个格式字符串和任意数量的参数,然后在运行时遍历堆栈以检索与%s、%d等相应的“参数”。这是一种对系统的攻击类型。 - Eagle
显示剩余3条评论

5

不要将输入字符串作为printf函数的格式参数。

为了正常工作,它必须没有任何"%..."项。这些是特殊命令,用于访问参数列表。


1
printfscanf函数族的format参数中,%被视为转义字符。你的原始输入中有一些这样的字符,而printf试图对它们进行解释。

不想挑剔,但也许“元字符”可能是一个更好的术语。"\n"是“转义字符”的一个例子。"%%"也是如此:http://en.wikipedia.org/wiki/Escape_sequences_in_C - paulsm4

0
如果您使用printf(string),则打印的字符串可能为NULL。
这是我看到的唯一问题。printf函数根据提供的格式读取所有va_list参数,因此如果传递NULL指针,则可能会崩溃。 使用LLVM 4.1时,我得到了这个警告:

Format string is not a literal string (potentially insecure)

这是我的个人意见:如果你确定字符串不是NULL(可以通过断言验证,然后在发布时删除该断言),那么你可以使用printf(string)。如果你不完全确定字符串可能为NULL或无效,则使用文字字符串。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接