145

C中的%n格式说明符有什么用?任何人都可以用一个例子来解释吗?

4

11 回答 11

212

这些答案中的大多数都解释了%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(并且可能会引入错误)是有争议的。)

于 2010-08-04T03:43:04.817 回答
162

什么都没有打印。参数必须是指向有符号整数的指针,其中存储了到目前为止写入的字符数。

#include <stdio.h>

int main()
{
  int val;

  printf("blah %n blah\n", &val);

  printf("val = %d\n", val);

  return 0;

}

前面的代码打印:

blah  blah
val = 5
于 2010-08-03T22:14:42.103 回答
19

我还没有真正看到说明符在现实世界中的许多实际用途%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

于 2010-08-03T22:37:34.810 回答
14

这里我们看到它存储了到目前为止打印的字符数。

nfprintf()参数应该是一个指向整数的指针,其中写入了到目前为止通过调用其中一个函数 而写入输出的字节数。没有参数被转换。

一个示例用法是:

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

n_chars那么将有一个值12

于 2010-08-03T22:17:01.997 回答
13

与 关联的参数%n将被视为 ,int*并用在该点打印的总字符数填充printf

于 2010-08-03T22:15:13.117 回答
13

到目前为止,所有的答案都是关于这样%n做的,但不是为什么有人会首先想要它。我发现sprintf/有点有用snprintf,当您可能需要稍后分解或修改结果字符串时,因为存储的值是结果字符串的数组索引。但是,使用 时,此应用程序更有用sscanf,尤其是因为该scanf系列中的函数不返回已处理的字符数,而是返回字段数。

另一个真正骇人听闻的用途是同时免费获得一个伪 log10,同时打印一个数字作为另一个操作的一部分。

于 2010-08-04T12:16:04.960 回答
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);
于 2015-08-10T10:42:31.813 回答
2

它不打印任何东西。它用于计算在格式字符串中出现之前打印了多少个字符%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

于 2010-08-03T22:23:09.543 回答
1

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

例子:

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

该程序的输出将是

Hello World
Characters printed so far = 12
于 2013-02-19T04:08:22.220 回答
-1

那些想使用 %n 格式说明符的人可能想看看这个:

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

抽象的

粗心地使用"%n"格式字符串可能会引入漏洞。

描述

滥用格式字符串可能导致多种漏洞。其中大部分在其他地方都有介绍,但本文档涵盖了一种特定类型的格式字符串漏洞,这种漏洞对于格式字符串来说是完全独特的。公开的文档对这些漏洞的覆盖范围不一致。

在 C 中,在 printf() 和 sprintf() 类型函数中使用“%n”格式规范可以更改内存值。这些格式的不适当设计/实现可能导致内存内容更改产生的漏洞。许多格式漏洞,尤其是那些具有“%n”以外的说明符的漏洞,会导致传统的故障,例如分段错误。“%n”说明符产生了更具破坏性的漏洞。“%n”漏洞可能具有次要影响,因为它们也可能是计算和网络资源的重要消耗者,因为可能必须传输大量数据才能为漏洞利用生成所需的指针值。

避免使用"%n"格式说明符。使用其他方式来实现您的目的。

来源:链接

于 2020-12-09T13:14:03.187 回答
-6

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

于 2010-08-04T22:33:25.280 回答