printf(str) 和 fwrite(str, 1, strlen(str), stdout) 之间有什么明显的区别吗?

nne*_*neo 6 c printf compiler-optimization language-lawyer

当打印静态、无格式字符串时,C 编译器执行的常见优化之一是将调用转换printf("foobar\n");为等效的puts("foobar");. 只要不使用返回值,这就是有效的(C 指定printf成功时返回写入的字符数,但puts成功时仅返回非负值)。C 编译器还会将调用转换fprintf(stdout, "foobar")fwrite("foobar", 1, 6, stdout).

但是,printftoputs优化仅适用于字符串以换行符结尾的情况,因为puts会自动附加换行符。如果没有,我希望printf可以优化为等效的fwrite,就像这种情况一样fprintf- 但编译器似乎不这样做。例如,以下代码(Godbolt链接):

#include <stdio.h>

int main() {
    printf("test1\n");
    printf("test2");
    fprintf(stdout, "test3");
}
Run Code Online (Sandbox Code Playgroud)

优化为汇编中的以下调用顺序:

puts("test1");
printf("test2");
fwrite("test3", 1, 5, stdout);
Run Code Online (Sandbox Code Playgroud)

我的问题是:为什么编译器在没有终止换行符的情况下不优化或printf类似?这只是一个错过的优化,还是与静态、无格式字符串一起使用时fwrite存在语义差异?如果相关,我正在寻找适用于 C11 或任何更新标准的答案。printffwrite

zwo*_*wol 6

这只是一个错过的优化\xe2\x80\x94编译器没有理由不能完成你想象的转换\xe2\x80\x94但它是一个有动机的优化。对于编译器来说,将调用转换为printf对的调用puts比对fputsor的调用要容易得多fwrite,因为后两者需要编译器stdout作为参数提供。 stdout是一个宏,当编译器开始进行库调用优化时,宏定义可能不再可用(即使集成了预处理器),或者可能无法再将标记序列解析为 AST 片段。

\n

相反,编译器可以轻松地转换fprintffputs,因为它也可以使用提供的任何内容作为 调用withFILE*的参数。但我不会对编译器可能会变成但不会变成...感到惊讶,因为它无法知道此调用的第一个参数stdout。(请记住,此优化过程适用于 IR 等价物或类似的东西。)fprintffputsfprintf(stdout, "blah\\n")fputs("blah\\n", stdout)puts("blah")fprintf&_iob[1]

\n