首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >内联函数机制

内联函数机制
EN

Stack Overflow用户
提问于 2013-10-27 20:34:11
回答 4查看 239关注 0票数 1

我知道内联函数不使用堆栈来复制参数,但它只是在被调用的任何地方替换函数体。

考虑以下两个函数:

代码语言:javascript
复制
inline void add(int a) {
   a++; 
} // does nothing, a won't be changed
inline void add(int &a) {
   a++; 
} // changes the value of a

如果堆栈不用于发送参数,编译器如何知道变量是否会被修改?替换这两个函数的调用后,代码是什么样子的?

EN

回答 4

Stack Overflow用户

发布于 2013-10-27 21:21:55

是什么让你认为有一个堆栈?即使有,是什么让你认为它会用来传递参数呢?

你必须明白有两个层次的推理:

language level:这里定义了应该发生的事情的语义,并且定义了机器级别: where

将语义编码到CPU指令中。

在语言级别,如果通过非常数引用传递参数,则该参数可能会被函数修改。语言层不知道这个神秘的“栈”是什么。注意:inline关键字对函数调用是否内联几乎没有影响,它只是说明定义是内联的。

在机器级别..。有许多方法可以实现这一点。在进行函数调用时,您必须遵守调用约定。该约定定义了如何在调用者和被调用者之间交换函数参数(和返回类型),以及其中谁负责保存/恢复CPU寄存器。一般而言,由于它是如此低级,因此此约定在每个CPU系列的基础上都会发生变化。

例如,在x86上,两个参数将直接传递到CPU寄存器(如果合适),而其余参数(如果有)将传递到堆栈。

票数 0
EN

Stack Overflow用户

发布于 2013-10-27 21:35:32

我已经检查过,如果强制它内联这些方法,至少GCC对它做了什么:

代码语言:javascript
复制
inline static void add1(int a) __attribute__((always_inline)); 
void add1(int a) {
   a++; 
} // does nothing, a won't be changed

inline static void add2(int &a) __attribute__((always_inline));
void add2(int &a) {
   a++; 
} // changes the value of a

int main() {

label1:
    int b = 0;
    add1(b);

label2:
    int a = 0;
    add2(a);

    return 0;
}

它的汇编输出如下所示:

代码语言:javascript
复制
.file   "test.cpp"
.text
.globl  main
.type   main, @function
main:
.LFB2:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $16, %esp
.L2:
    movl    $0, -4(%ebp)
    movl    -4(%ebp), %eax
    movl    %eax, -8(%ebp)
    addl    $1, -8(%ebp)
.L3:
    movl    $0, -12(%ebp)
    movl    -12(%ebp), %eax
    addl    $1, %eax
    movl    %eax, -12(%ebp)
    movl    $0, %eax
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
.LFE2:

有趣的是,即使是add1()的第一次调用实际上没有在函数调用之外做任何事情,它也没有优化出来。

票数 0
EN

Stack Overflow用户

发布于 2013-10-27 22:09:46

如果堆栈不用于发送参数,编译器如何知道变量是否会被修改?

正如Matthieu M.已经指出的,语言构造本身对内联一无所知,将stack.You关键字指定给函数只是为了给编译器一个提示,并表达你希望这个例程是内联的愿望。如果发生这种情况,完全取决于编译器。

编译器尝试在特定情况下预测此过程的优点。如果编译器认为内联函数会使代码变慢或变得不可接受地大,它就不会内联它。或者,如果由于语法依赖而无法执行,例如其他代码使用函数指针进行回调,或者将函数导出到外部,如在动态/静态代码库中。

替换这两个函数的调用后,代码是什么样子的?

在编译时,这些函数都没有内联

代码语言:javascript
复制
g++ -finline-functions -S main.cpp

您可以看到它,因为在main的反汇编过程中

代码语言:javascript
复制
void add1(int a) {
    a++;
}
void add2(int &a) {
   a++; 
}

inline void add3(int a) {
   a++; 
} // does nothing, a won't be changed

inline void add4(int &a) {
   a++; 
} // changes the value of a

inline int f() { return 43; }

int main(int argc, char** argv) {

    int a = 31;
    add1(a);
    add2(a);
    add3(a);
    add4(a);
    return 0;
}

我们看到每个例程都有一个调用

代码语言:javascript
复制
main:
.LFB8:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        subq    $32, %rsp
        movl    %edi, -20(%rbp)
        movq    %rsi, -32(%rbp)
        movl    $31, -4(%rbp)
        movl    -4(%rbp), %eax
        movl    %eax, %edi
        call    _Z4add1i        // function call
        leaq    -4(%rbp), %rax
        movq    %rax, %rdi
        call    _Z4add2Ri       // function call
        movl    -4(%rbp), %eax
        movl    %eax, %edi
        call    _Z4add3i        // function call
        leaq    -4(%rbp), %rax
        movq    %rax, %rdi
        call    _Z4add4Ri       // function call
        movl    $0, %eax
        leave
        ret
        .cfi_endproc

使用-O1编译将从程序中删除所有函数,因为它们什么也不做。然而,添加了

代码语言:javascript
复制
__attribute__((always_inline))

允许我们看到代码内联时会发生什么:

代码语言:javascript
复制
void add1(int a) {
    a++;
}

void add2(int &a) {
   a++; 
}

inline static void add3(int a) __attribute__((always_inline));
inline void add3(int a) {
   a++; 
} // does nothing, a won't be changed

inline static void add4(int& a) __attribute__((always_inline));
inline void add4(int &a) {
   a++; 
} // changes the value of a

int main(int argc, char** argv) {

    int a = 31;
    add1(a);
    add2(a);
    add3(a);
    add4(a);
    return 0;
}

现在:g++ -finline-functions -S main.cpp结果如下:

代码语言:javascript
复制
main:
.LFB9:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        pushq   %rbp
        .cfi_def_cfa_offset 16
        movq    %rsp, %rbp
        .cfi_offset 6, -16
        .cfi_def_cfa_register 6
        subq    $32, %rsp
        movl    %edi, -20(%rbp)
        movq    %rsi, -32(%rbp)
        movl    $31, -4(%rbp)
        movl    -4(%rbp), %eax
        movl    %eax, %edi
        call    _Z4add1i        // function call
        leaq    -4(%rbp), %rax
        movq    %rax, %rdi
        call    _Z4add2Ri       // function call
        movl    -4(%rbp), %eax
        movl    %eax, -8(%rbp)
        addl    $1, -8(%rbp)    // addition is here, there is no call
        movl    -4(%rbp), %eax
        addl    $1, %eax        // addition is here, no call again
        movl    %eax, -4(%rbp)
        movl    $0, %eax
        leave
        ret
        .cfi_endproc
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19618075

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档