问题 printf在同一个调用中%n的值 - 无意义?


我无法找出以下部分的意图 printf 规格在 cppreference.com

每次转换操作后都有一个序列点   符;这允许存储多个%n个结果   变量和 在%n之前打印%n存储的值   呼叫。

这读起来好像是一个(甚至几个)的结果 %n 转换说明符可以打印出来 printf-声明。 但我无法弄清楚如何实现这一点,因为所有的争论都传递给了 printf 以前评估过 printf输入正文(参数评估后有一个序列点)。因此,a的变量的值 %n 写之前会被评估 printf 有可能用“到目前为止写入的字符数”覆盖此变量的值:

#include <stdio.h>

int main( int argc, char* argv[] )
{
    int n = 0;
    printf("Hello, world!%n (%d first n); %n (%d second n)", &n ,n, &n, n);
    // will print out "Hello, world! (0 first n);  (0 second n)"

    return 0;
}

我的问题:如果没有任何机会“在同一个呼叫中早先打印%n存储的值”,那么不是相应部分的 printf 规格毫无意义或误导?

实际意义是什么? c99标准 声明:

7.19.6格式化输入/输出功能   (1)格式化的输入/输出功能应该表现得好像在后面有一个序列点   与每个说明符关联的操作。

是否减少了获取未定义行为的“机会”?

问题用c ++和c标记,因为我认为这个主题对两种语言应用相同的方式。


11568
2018-02-25 00:11


起源

C11 ref是 7.21.6 1。 - chux
cppreference现在不那么无意义了 - Cubbi


答案:


由于您已正确识别的原因,您的代码确实只打印了零。

标准中的陈述仍然是必要的,因为在其他地方采用全面措辞,如果一个对象被写入不止一次而没有插入序列点,则程序的行为是不确定的。实际上,声明必须说你的代码没有未定义的行为(不像,比方说, i = i++;)。


4
2018-02-25 00:13



对于说有一个序列点的部分来说,这是正确的。但是他用粗体突出显示的部分有什么意义呢?这不太可能,是吗? - Barmar
@Barmar:嗯,当然你不能 读 较早的写道,但粗体部分是序列点的唯一重要结果!如果您只写入不同的输出位置,则不需要序列点。 - Kerrek SB
@Kerrek这是否意味着printf()不能写成普通的用户定义函数? - Neil Butterworth
@NeilButterworth不,它没有。语句之间有一个序列点,所以它需要的是存储到每个语句 %n 可以用不同的陈述来完成。 - Barmar
@NeilButterworth并表示执行 printf() 从左到右处理它们。 - Barmar


这可能很疯狂,但我认为以下是合法的:

char s[2];
s[1] = '\0';
printf("Hi, world!%hhn%s", s, s);

%hhn 获取指向char的指针。它写道 10 (到目前为止写的字符数) s[0]。然后它打印字符串 s,相当于 "\n" 要么 (char[]){ 10, 0 } (假设是ASCII)。


8
2018-02-25 00:18



如果C标准不合法,我不知道我们会怎么做。 :) - Barmar
这很疯狂,但也很合理。 - Jonathan Leffler
创造性的方法:-)一个小问题是 hh C ++支持,但C不支持。它仍然让我假设打印一个 %n 狭义上的价值(即数字而不是ASCII字符)是不可能的,因此cppreference.com的大胆部分具有误导性或无意义,对吧?无论如何,创意+1! - Stephan Lechner
@StephanLechner hh 至少从1999年开始支持C语言。 - melpomene
@melpomene:根据标准你是对的。 cppreference.com上有一个注释“(c ++ 11)” hh (但不是 h)。所以我认为自c11以来它得到了支持...... - Stephan Lechner


可以想象一个编译器将一个调用转换为 printf 进入一系列单独的呼叫 fputs() 使用通过调用转换处理程序计算的字符串片段。此实现可能会将值存储到 n 之前的价值 n 得到印刷。这会不符合规定吗?

现代编译器已经对其进行了小规模的优化 printf()如转换 printf("Hello world\n"); 成 puts("Hello world"); 和 printf("\n"); 成 fputchar('\n');。它们还检查格式字符串和参数的一致性......进一步的优化将导致上述情况。


2
2018-02-25 00:31



“这会不符合规定吗?“ - 是的。编译必须保留语义。 - melpomene
我认为它不符合要求,因为 - 即使对于像printf这样的库函数 - 在输入函数代码之前也会对参数进行求值。并且优化不能改变这种语义,对吧? - Stephan Lechner