Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/62.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么printf在使用MinGW-w64时会生成一个额外的函数?_C++_C_Gcc_Mingw_Mingw W64 - Fatal编程技术网

C++ 为什么printf在使用MinGW-w64时会生成一个额外的函数?

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

通过使用-O2查看GCC的程序集输出,我发现如果使用
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
    的损坏名称。看

  • printf函数有多个变体,以及一个或几个实现该功能的函数。后一个函数的名称以
    \uuuuu
    开头,执行繁重的任务,并且是实现的内部功能;向用户公开的函数不做任何重要的工作,它们基本上只是将调用转发到另一个转发函数或实现函数。看

  • 在您的案例中,转发函数的内联失败。看

  • 根据您的评论,我怀疑您真正的问题是为什么内联失败,为什么没有直接调用实现而没有任何开销。好问题,我看不出任何有效的技术原因

    我知道,如果不使用链接时间优化,gcc(至少在Linux上)倾向于不内联函数,即使函数体(实现)和调用站点位于同一个翻译单元(在同一个源文件中)

    尝试启用链接时间优化。请参见。中的
    -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
        [...]