我知道内联函数不使用堆栈来复制参数,但它只是在被调用的任何地方替换函数体。
考虑以下两个函数:
inline void add(int a) {
a++;
} // does nothing, a won't be changed
inline void add(int &a) {
a++;
} // changes the value of a如果堆栈不用于发送参数,编译器如何知道变量是否会被修改?替换这两个函数的调用后,代码是什么样子的?
发布于 2013-10-27 21:21:55
是什么让你认为有一个堆栈?即使有,是什么让你认为它会用来传递参数呢?
你必须明白有两个层次的推理:
language level:这里定义了应该发生的事情的语义,并且定义了机器级别: where
将语义编码到CPU指令中。
在语言级别,如果通过非常数引用传递参数,则该参数可能会被函数修改。语言层不知道这个神秘的“栈”是什么。注意:inline关键字对函数调用是否内联几乎没有影响,它只是说明定义是内联的。
在机器级别..。有许多方法可以实现这一点。在进行函数调用时,您必须遵守调用约定。该约定定义了如何在调用者和被调用者之间交换函数参数(和返回类型),以及其中谁负责保存/恢复CPU寄存器。一般而言,由于它是如此低级,因此此约定在每个CPU系列的基础上都会发生变化。
例如,在x86上,两个参数将直接传递到CPU寄存器(如果合适),而其余参数(如果有)将传递到堆栈。
发布于 2013-10-27 21:35:32
我已经检查过,如果强制它内联这些方法,至少GCC对它做了什么:
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;
}它的汇编输出如下所示:
.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()的第一次调用实际上没有在函数调用之外做任何事情,它也没有优化出来。
发布于 2013-10-27 22:09:46
如果堆栈不用于发送参数,编译器如何知道变量是否会被修改?
正如Matthieu M.已经指出的,语言构造本身对内联一无所知,将stack.You关键字指定给函数只是为了给编译器一个提示,并表达你希望这个例程是内联的愿望。如果发生这种情况,完全取决于编译器。
编译器尝试在特定情况下预测此过程的优点。如果编译器认为内联函数会使代码变慢或变得不可接受地大,它就不会内联它。或者,如果由于语法依赖而无法执行,例如其他代码使用函数指针进行回调,或者将函数导出到外部,如在动态/静态代码库中。
替换这两个函数的调用后,代码是什么样子的?
在编译时,这些函数都没有内联
g++ -finline-functions -S main.cpp您可以看到它,因为在main的反汇编过程中
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;
}我们看到每个例程都有一个调用:
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编译将从程序中删除所有函数,因为它们什么也不做。然而,添加了
__attribute__((always_inline))允许我们看到代码内联时会发生什么:
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结果如下:
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_endprochttps://stackoverflow.com/questions/19618075
复制相似问题