C中的%n
格式说明符有什么用?任何人都可以用一个例子来解释吗?
11 回答
这些答案中的大多数都解释了%n
它的作用(即不打印任何内容并将迄今为止打印的字符数写入int
变量),但到目前为止,还没有人真正给出它有什么用途的例子。这是一个:
int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
将打印:
hello: Foo
Bar
Foo 和 Bar 对齐。(如果不使用%n
这个特定的例子,这样做是微不足道的,而且通常总是可以打破第一次printf
调用:
int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");
稍微增加的便利性是否值得使用深奥的东西%n
(并且可能会引入错误)是有争议的。)
什么都没有打印。参数必须是指向有符号整数的指针,其中存储了到目前为止写入的字符数。
#include <stdio.h>
int main()
{
int val;
printf("blah %n blah\n", &val);
printf("val = %d\n", val);
return 0;
}
前面的代码打印:
blah blah
val = 5
我还没有真正看到说明符在现实世界中的许多实际用途%n
,但我记得很久以前它曾用于老式 printf 漏洞和格式字符串攻击。
事情是这样的
void authorizeUser( char * username, char * password){
...code here setting authorized to false...
printf(username);
if ( authorized ) {
giveControl(username);
}
}
恶意用户可以利用 username 参数作为格式字符串传递给 printf 并使用%d
,%c
或 w/e 的组合来遍历调用堆栈,然后将授权的变量修改为真值。
是的,这是一个深奥的用途,但在编写守护程序以避免安全漏洞时知道总是有用的?:D
从这里我们看到它存储了到目前为止打印的字符数。
n
fprintf()
参数应该是一个指向整数的指针,其中写入了到目前为止通过调用其中一个函数 而写入输出的字节数。没有参数被转换。
一个示例用法是:
int n_chars = 0;
printf("Hello, World%n", &n_chars);
n_chars
那么将有一个值12
。
与 关联的参数%n
将被视为 ,int*
并用在该点打印的总字符数填充printf
。
到目前为止,所有的答案都是关于这样%n
做的,但不是为什么有人会首先想要它。我发现sprintf
/有点有用snprintf
,当您可能需要稍后分解或修改结果字符串时,因为存储的值是结果字符串的数组索引。但是,使用 时,此应用程序更有用sscanf
,尤其是因为该scanf
系列中的函数不返回已处理的字符数,而是返回字段数。
另一个真正骇人听闻的用途是同时免费获得一个伪 log10,同时打印一个数字作为另一个操作的一部分。
前几天,我发现自己的处境%n
可以很好地解决我的问题。与我之前的回答不同,在这种情况下,我无法设计一个好的替代方案。
我有一个显示一些指定文本的 GUI 控件。这个控件可以用粗体(或斜体,或下划线等)显示部分文本,我可以通过指定开始和结束字符索引来指定哪一部分。
就我而言,我正在使用 生成控件的文本snprintf
,并且我希望将其中一个替换设置为粗体。找到此替换的开始和结束索引并非易事,因为:
该字符串包含多个替换,其中一个替换是任意的、用户指定的文本。这意味着对我关心的替换进行文本搜索可能是模棱两可的。
格式字符串可能是本地化的,并且它可能使用
$
POSIX 扩展作为位置格式说明符。因此,在原始格式字符串中搜索格式说明符本身并非易事。本地化方面也意味着我不能轻易地将格式字符串分解为对
snprintf
.
因此,找到围绕特定替换的索引的最直接方法是:
char buf[256];
int start;
int end;
snprintf(buf, sizeof buf,
"blah blah %s %f yada yada %n%s%n yakety yak",
someUserSpecifiedString,
someFloat,
&start, boldString, &end);
control->set_text(buf);
control->set_bold(start, end);
它不打印任何东西。它用于计算在格式字符串中出现之前打印了多少个字符%n
,并将其输出到提供的 int:
#include <stdio.h>
int main(int argc, char* argv[])
{
int resultOfNSpecifier = 0;
_set_printf_count_output(1); /* Required in visual studio */
printf("Some format string%n\n", &resultOfNSpecifier);
printf("Count of chars before the %%n: %d\n", resultOfNSpecifier);
return 0;
}
它将存储到目前为止在该printf()
函数中打印的字符数的值。
例子:
int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);
该程序的输出将是
Hello World
Characters printed so far = 12
那些想使用 %n 格式说明符的人可能想看看这个:
不要使用"%n"
格式字符串说明符
抽象的
粗心地使用"%n"
格式字符串可能会引入漏洞。
描述
滥用格式字符串可能导致多种漏洞。其中大部分在其他地方都有介绍,但本文档涵盖了一种特定类型的格式字符串漏洞,这种漏洞对于格式字符串来说是完全独特的。公开的文档对这些漏洞的覆盖范围不一致。
在 C 中,在 printf() 和 sprintf() 类型函数中使用“%n”格式规范可以更改内存值。这些格式的不适当设计/实现可能导致内存内容更改产生的漏洞。许多格式漏洞,尤其是那些具有“%n”以外的说明符的漏洞,会导致传统的故障,例如分段错误。“%n”说明符产生了更具破坏性的漏洞。“%n”漏洞可能具有次要影响,因为它们也可能是计算和网络资源的重要消耗者,因为可能必须传输大量数据才能为漏洞利用生成所需的指针值。
避免使用"%n"
格式说明符。使用其他方式来实现您的目的。
来源:链接
%n 是 C99,不适用于 VC++。