通过-O2查看GCC的程序集输出我看到如果我使用printf
,GCC将创建一个名为_Z6printfPKcz
的函数,然后调用__mingw_vprintf
。这个间接的目的是什么?为什么printf
不会直接转换为对__mingw_printf
的调用?其他相关功能也是如此,例如sprintf。
编辑:
为了记录,我知道修剪的名称是什么以及它是如何工作的。我的问题是为什么编译器首先生成函数,这个函数除了将参数转发给__mingw_vprintf之外什么都不做,只需直接调用__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
...
答案 0 :(得分:3)
_Z6printfPKcz
只是printf
的错误名称 - 搜索“name mangling”以获取更多信息。这是实际的printf功能。
__mingw_vprintf
是printf
调用的内部函数 - 不要求printf
不调用其他函数来完成其工作。也许__mingw_vprintf
会处理所有格式和打印,而printf
的编写方式如下:
int printf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
int result = __mingw_vprintf(fmt, va);
va_end(va);
return result;
}
答案 1 :(得分:2)
_pZ6printfPKcz
是具有签名printf(char const*, ...)
的函数的C ++错位名称 - 换句话说,就是实际的printf函数。其中,它似乎是通过调用另一个函数来实现的。由于printf
有六种不同的变体(具有不同类型的输出,例如输出到文件,输出到字符串,输出到控制台等),因此有一个单一的&##是有意义的34;做printf"功能 - 在这种情况下似乎是__mingq_vprintf
。由于printf实现是几千行代码,我们并不是真的想要为所有不同的变体重复6次左右(我知道,因为在工作中我一直在实现printf的版本对于OpenCL来说 - 它是一个非常复杂的功能,你绝对不想多次使用实际代码)。
您可以在" stdio.h"中看到printf
的确切定义。这里:
http://sourceforge.net/apps/trac/mingw-w64/browser/trunk/mingw-w64-headers/crt/stdio.h?rev=5437#L283
至于为什么他们这样做,你真的需要问开发人员mingw - 但我的假设是他们试图保持不同变体的数量 - 例如{{1 }}也用于实现实际的__mingw_vprintf
变体,因此已经有一半的变体"。
额外的分层还允许printf在窄(ASCII)和宽字符(所谓的UNICODE)之间切换,如下面的宏所示:
当你意识到__mingw_vprintf变成另一层呼叫时,你会更加讨厌mingw开发者:
然而,在printf实现的整个方案中,就像我在评论中所说的那样,它的开销非常小 - vprintf
代码长达2000行:
(比glibc实现小得多)
答案 2 :(得分:1)
在这种特定情况下,Printf无法内联。如果内联失败,编译器必须创建一个显式函数体。成功的内联解释了没有间接的常规行为。
答案 3 :(得分:1)
这里有三件事需要实现,而且没有一个答案包含所有三个(至少目前)。
_Z6printfPKcz
是printf
的错位名称。请参阅immibis' answer。
printf
函数有多种变体,一个或几个函数实现功能。后面的函数(名称以__
开头)执行繁重,并且是实现的内部函数;暴露给用户的功能不会做任何重要的工作,它们基本上只是将呼叫转发到另一个转发功能或实现功能。请参阅Mats Petersson's answer。
在您的情况下,转发功能的内联失败。请参阅Basilevs' answer。
我怀疑,根据您的评论,您真正的问题是内联失败的原因,为什么不直接调用实现,没有任何开销。好问题,我没有看到任何有效的技术原因。
我知道gcc(至少在Linux上)如果不使用链接时优化就不倾向于内联函数,即使函数体(实现)和调用站点都在同一个翻译单元中(在相同的源文件)。
尝试启用链接时优化。请参阅Options That Control Optimization中的-flto
。很可能会通过链接时间优化内联调用;这些功能似乎是内联的良好候选者。
更新:我无法测试mingw;这是我在Linux上的测试。这段代码:
#include <stdio.h>
int main(int argc, char** argv) {
printf("%d\n", argc);
}
导致以下程序集已禁用优化-O0
:
[...]
call printf
[...]
也就是说,对printf
的调用没有内联。但是,已经在-O1
我得到了:
[...]
call __printf_chk
[...]
这意味着对printf
的调用已内联,我直接调用底层实现。它甚至不需要链接时间优化。您只需要激励编译器进行内联。
考虑一下:编译器可能不会在C 运行时中的编译时内联函数,因为它们应该在运行时中访问。但它肯定会在-O1
已经内联包装函数。
至于为什么特定平台上的特定版本的编译器无法内联特定功能,那么......如果您认为它在您的应用程序中受到性能影响,请尝试提交错误报告。如果我是你,我不会太担心它,由于内联失败,IO将比你额外的函数调用花费更多的时间。