C++ 为什么printf在使用MinGW-w64时会生成一个额外的函数?
通过使用-O2查看GCC的程序集输出,我发现如果使用C++ 为什么printf在使用MinGW-w64时会生成一个额外的函数?,c++,c,gcc,mingw,mingw-w64,C++,C,Gcc,Mingw,Mingw W64,通过使用-O2查看GCC的程序集输出,我发现如果使用printfGCC将创建一个名为\u Z6printfPKcz的函数,然后调用\u mingw\u vprintf。这间接的目的是什么?为什么printf不直接转换为调用\uuuu mingw\uprintf?对于其他相关函数,如sprintf,也是如此 编辑: 作为记录,我知道mangling是什么名字以及它是如何工作的。我的问题是,为什么编译器首先要生成函数,一个只将参数转发给uu mingw_vprintf的函数,而它可以直接调用u mi
printf
GCC将创建一个名为\u Z6printfPKcz
的函数,然后调用\u mingw\u vprintf
。这间接的目的是什么?为什么printf
不直接转换为调用\uuuu mingw\uprintf
?对于其他相关函数,如sprintf,也是如此
编辑:
作为记录,我知道mangling是什么名字以及它是如何工作的。我的问题是,为什么编译器首先要生成函数,一个只将参数转发给uu mingw_vprintf的函数,而它可以直接调用u mingw_printf并保存不必要的间接操作
换句话说,为什么要这样做:
printf("%d\n", var);
为此编译:
_Z6printfPKcz:
sub rsp, 56
mov QWORD PTR 72[rsp], rdx
lea rdx, 72[rsp]
mov QWORD PTR 80[rsp], r8
mov QWORD PTR 88[rsp], r9
mov QWORD PTR 40[rsp], rdx
call __mingw_vprintf
add rsp, 56
ret
main:
...
call _Z6printfPKcz
...
这什么时候足够
main:
...
call __mingw_printf
...
\u Z6printfPKcz
只是printf
的损坏名称-请搜索“名称损坏”以了解更多信息。这是实际的printf函数
\uuuu mingw\u vprintf
是一个内部函数,它调用printf
——不要求printf
不调用另一个函数来完成其工作。也许\uuuu mingw\uvprintf
处理所有的格式化和打印,而printf
是这样写的:
int printf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int result = __mingw_vprintf(fmt, va);
va_end(va);
return result;
}
<代码> pZ6Primtfpkcz 是一个函数的C++名称,该函数具有签名>代码PrtTf(char const *,…)< /> > -换句话说,实际的PrtTf函数。这似乎是通过调用另一个函数实现的。由于有六种不同的
printf变体
(具有不同类型的输出,例如输出到文件、输出到字符串、输出到控制台等),因此有一个单独的“do printf”函数是有意义的,在这种情况下,它似乎是\u mingq\u vprintf
。由于printf实现只有几千行代码,所以我们不想对所有不同的变体重复6次左右(我知道,因为在工作中我一直在为OpenCL实现printf版本-这是一个相当复杂的函数,您肯定不想将实际代码重复几次)
您可以在这里的“stdio.h”中看到printf
的确切定义:
至于他们这样做的确切原因,这确实是你必须问mingw的开发人员的问题——但我的假设是,他们正在努力减少不同变体的数量——例如,\uu mingw\u vprintf
也被用于实现实际的vprintf
变体,因此“已经有一半的变体了”
额外的分层还允许printf在窄字符(ASCII)和宽字符(称为UNICODE)之间切换,如下面的宏所示:
当你意识到uu mingw_vprintf变成了另一层调用时,你会更恨mingw开发人员:
然而,在printf实现的整个方案中,正如我在评论中所说的,它的开销非常小,\u pformat
代码是一个很好的2000行代码:
(比glibc实现小得多)Printf在这种特定情况下无法内联。若内联失败,编译器必须创建一个显式函数体。通常没有间接寻址的行为可以通过成功的内联来解释。这里有三件事需要实现,没有一个答案包含所有这三件事(至少目前是这样)
\u Z6printfPKcz
是printf
的损坏名称。看\uuuuu
开头,执行繁重的任务,并且是实现的内部功能;向用户公开的函数不做任何重要的工作,它们基本上只是将调用转发到另一个转发函数或实现函数。看-flto
,该调用很可能与链路时间优化内联;该函数似乎是内联的一个很好的候选者
更新:我无法测试mingw;下面是我在Linux上的测试。此代码:
#include <stdio.h>
int main(int argc, char** argv) {
printf("%d\n", argc);
}
也就是说,对printf
的调用不是内联的。然而,在-O1
我已经得到:
[...]
call __printf_chk
[...]
这意味着对printf
的调用是内联的,我直接调用底层实现。它甚至不需要优化链接时间。您只需要激励编译器进行内联
考虑一下:编译器可能不会在编译时内联C运行时中的函数,因为它们应该在运行时访问。但它肯定在-O1
中已经内联了包装器函数
至于为什么特定平台上特定版本的编译器无法内联特定函数,那么。。。如果您认为这对应用程序的性能有影响,请尝试提交错误报告。如果我是你,我就不会太担心它,由于内联失败,IO将比额外的函数调用花费你更多的时间。看起来标准的方法
printf
是im
[...]
call __printf_chk
[...]