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

jos*_*osh 116 c printf

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

jam*_*lin 178

大多数这些答案的解释%n (这是打印不信邪,写迄今为止打印到一个字符数int变量),但至今没有人真正给出了什么样的例子使用它.这是一个:

int n;
printf("%s: %nFoo\n", "hello", &n);
printf("%*sBar\n", n, "");
Run Code Online (Sandbox Code Playgroud)

将打印:

hello: Foo
       Bar
Run Code Online (Sandbox Code Playgroud)

与Foo和Bar对齐.(如果不使用%n这个特定的例子,这样做是微不足道的,一般来说,总是可以打破第一次printf调用:

int n = printf("%s: ", "hello");
printf("Foo\n");
printf("%*sBar\n", n, "");
Run Code Online (Sandbox Code Playgroud)

稍微增加的便利性是否值得使用类似的东西%n(并且可能引入错误)值得商榷.)

  • @AndrewS`&n`是一个指针(`&`是运算符的地址); 指针是必要的,因为C是按值传递的,没有指针,`printf`无法修改`n`的值.`printf`格式字符串中的`%*s`用法使用*n字符的*字段宽度*打印`%s`说明符(在本例中为空字符串`""`).基本的'printf`原则的解释基本上超出了这个问题的范围(和答案); 我建议阅读`printf`文档或在SO上询问你自己的单独问题. (9认同)
  • 哦,我的 - 这是一个基于字符的版本,用于计算给定字体中字符串的像素大小! (3认同)
  • 感谢您展示用例.我不明白为什么人们只是将手册复制粘贴到SO中并有时重新编写.我们是人类,一切都是为了**理由*应该总是在答案中解释."什么都不做"就像是说"酷酷一词意味着酷" - 几乎无用的知识. (2认同)

Sta*_*key 145

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

#include <stdio.h>

int main()
{
  int val;

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

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

  return 0;

}
Run Code Online (Sandbox Code Playgroud)

以前的代码打印:

blah  blah
val = 5
Run Code Online (Sandbox Code Playgroud)

  • @Jack`int`总是被签名。 (3认同)

Xzh*_*hsh 18

我还没有真正看到%n说明符的许多实际的现实世界用途,但我记得它在oldschool printf漏洞中使用了很长一段时间的格式字符串攻击.

有点像这样的东西

void authorizeUser( char * username, char * password){

    ...code here setting authorized to false...
    printf(username);

    if ( authorized ) {
         giveControl(username);
    }
}
Run Code Online (Sandbox Code Playgroud)

恶意用户可以利用username参数作为格式字符串传递给printf并使用组合%d,%c或者w/e通过调用堆栈,然后修改授权为真值的变量.

是的,它是一种深奥的用法,但是在编写守护进程以避免安全漏洞的时候总是有用的吗?:d

  • printf(用户名)要求格式字符串攻击. (7认同)
  • 除了“%n”之外,还有更多理由避免使用未经检查的输入字符串作为“printf”格式字符串。 (3认同)

KLe*_*ee1 13

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

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

一个示例用法是:

int n_chars = 0;
printf("Hello, World%n", &n_chars);
Run Code Online (Sandbox Code Playgroud)

n_chars然后会有一个值12.


Eva*_*van 9

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


R..*_*R.. 9

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

另一个真正的hackish使用是在同时打印一个数字作为另一个操作的一部分时免费获得伪log10.

  • @noloader:关于任何指针的使用都是如此.没有人说"坏人感谢你"写'*p = f();`.为什么`%n`,这只是将结果写入指针指向的对象的另一种方式,被认为是"危险的",而不是认为指针本身是危险的? (8认同)
  • @noloader:怎么样?*使用*`%n`绝对没有攻击者易受攻击的危险.错误的'%n'的臭名昭着真的属于传递消息字符串而不是格式字符串作为格式参数的愚蠢做法.当`%n`实际上是正在使用的有意格式字符串的一部分时,这种情况当然不会出现. (6认同)

jam*_*lin 7

前几天,我发现自己处在%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);
Run Code Online (Sandbox Code Playgroud)

  • @jww多个`snprintf`调用的问题是替换可能在其他语言环境中重新排列,因此不能像这样拆分它们。 (2认同)
  • @PSkocik *如果*您正在输出到终端。例如,如果您正在使用 Win32 富编辑控件,那么除非您想返回并在之后解析终端控件序列,否则这将无济于事。这也假设您希望在替换文本的其余部分中遵守终端控制序列;如果你不这样做,那么你就必须过滤或逃避那些。我并不是说没有 `%n` 就不可能;我声称使用 `%n` 比替代方法更直接。 (2认同)