在 C 语言中,% n 格式说明符的用途是什么?

%n格式说明符在 C 语言中的用途是什么? 有人能举个例子解释一下吗?

120981 次浏览

没有打印出来。参数必须是一个指向有符号整型数的指针,其中存储了到目前为止写入的字符数。

#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相关联的参数将被视为 int*,并用 printf中该点上打印的总字符数填充。

给你我们可以看到它存储了到目前为止打印的字符数。

这个参数应该是一个指向一个整数的指针,这个整数被写入到一个 fprintf()函数的调用到目前为止写入输出的字节数。没有参数被转换。

例如:

int n_chars = 0;
printf("Hello, World%n", &n_chars);

那么 n_chars的值就是 12

什么都不会印。它用于计算在 %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;
}

(_set_printf_count_output文档)

我还没有真正看到 %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 的组合来遍历调用堆栈,然后将授权的变量修改为真值。

是的,这是一个深奥的用法,但是在编写守护进程以避免安全漏洞时知道这一点总是有用的

这些答案大多解释了什么是 %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这样深奥的东西(可能还会引入错误)来增加一些方便性,这还有待讨论。)

到目前为止,所有的答案都是关于 %n的,但不是为什么会有人想要它摆在首位。对于 sprintf/snprintf,我发现它有些用处,因为存储的值是结果字符串的数组索引,所以稍后可能需要分解或修改结果字符串。然而,对于 sscanf,这个应用程序要有用得多,特别是因为 scanf家族中的函数不返回处理的字符数,而是返回字段数。

另一个非常糟糕的用法是在另一个操作的一部分打印数字的同时免费获得伪 log10。

% n 是 C99,不适用于 VC + + 。

它将存储到目前为止在该 printf()函数中打印的字符数的值。

例如:

int a;
printf("Hello World %n \n", &a);
printf("Characters printed so far = %d",a);

这个程序的输出将是

Hello World
Characters printed so far = 12

有一天,我发现 %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 格式说明符的用户可能需要查看以下内容:

不要使用 "%n"格式字符串说明符

在 C 中,在 printf ()和 sprintf ()中使用“% n”格式规范 类型函数可以更改内存值 这些格式的设计/实现可能会导致漏洞 由于内存内容的变化而产生, 特别是那些带有“% n”以外的说明符的,会导致 传统的故障,例如内存区段错误。“% n”说明符 已经产生了更多的破坏性漏洞。“% n”漏洞 可能产生次要影响,因为它们也可能是重要的 消费者的计算和网络资源,因为大 可能必须传输大量数据才能生成所需的 指针值。避免使用 "%n"格式 使用其他方法来达到你的目的。

资料来源: 链接