1、形参带默认值的函数
- 1、给默认值的时候,只能从右向左给!!!
- 2、调用效率的问题(少了一句move的调用)
- 3、函数定义处可以给形参默认值,函数声明处也可以给形参默认值
- 4、形参给默认值的时候,不管是函数定义处给,还是函数声明处给,每一个形参的默认值只能出现一次!!!哪怕值一样也不可以!!!
- 函数的声明可以无数次,但是定义只能一次。
上面这样写是错误的,因为编译器从上到下编译,形参默认值是从右向左给
上面这样可行。因为编译器从上到下编译代码,默认从右向左的顺序看,b=20,a=10
- 如果只传入参数a,则b就是使用默认值20
- 如果给a和b都添加默认值,那么在调用的时候就可以不用传参数了。
注意:
- 我们给参数是从左向右给,实际上压栈是从右向左压,所以给默认值的时候必须是从右向左给!!!
下面这种写法是错误的!!!、
调用效率的问题:
- 带形参的默认值函数效率增高!少了1句move指令
#include <iostream>
using namespace std;
//sum(, 20);这个写法是错误的
int sum(int a, int b = 20);
int sum(int a = 10, int b);
int main()
{
int a = 10;
int b = 20;
int ret = sum(a, b);
/*
mov eax, dword ptr[ebp-8]
push eax 入栈
mov ecx, dword ptr[ebp-4]
push ecx
call sum
*/
cout << "ret:" << ret << endl;
/*
push 14H
mov ecx, dword ptr[ebp-4]
push ecx
call sum
*/
ret = sum(a);
/*
push 14H
push 0Ah
call sum
*/
ret = sum();//和sum(20, 50);是一样的
ret = sum(a, 40);
/*
push 28H
mov ecx, dword ptr[ebp-4]
push ecx
call sum 这个和ret = sum(a);一样
*/
return 0;
}
int sum(int a, int b)
{
return a + b;
}
2、inline内联函数
面试问题:inline内联函数 和 普通函数的区别???
- 普通函数调用有标准的开销,介绍下函数的开销,我前面文章有讲过;
- 内联函数在函数的调用点直接把函数的代码进行展开处理了,没有函数的调用开销了;
- 如果函数在短时间内大量调用,可以将函数设置成内联函数
一个函数调用时,标准的函数调用过程:
- 从右向左压实参,压完实参后,执行call指令,将call指令的下一行地址入栈;
- 进入被调用函数中,sum的
{
,执行push ebp,将调用方main函数的地址入栈;然后给sum函数开辟栈帧,给sum函数栈帧初始化(linux下gcc和g++只负责开辟栈帧,不对帧的内存初始化) - …
以上也叫做 函数调用过程中的开销(实参压栈、栈帧开辟、栈帧回退)。
其实sum函数只有x+y,是非常简单的,只有mov,add,move这几个指令,但是实际上确需要有函数调用开销,一次不多,如果千万次调用,就开销很大了。
inline内联函数:
- 在编译过程中,就没有函数的调用开销了,在函数的调用点直接把函数的代码进行展开处理了!!!
- inline函数在符号表中不再生成相应的函数符号!!!因为在函数调用点直接展开了!!!
- 但是不是所有的inline都会被编译器处理成内联函数 - 比如说递归;因为编译器是不执行代码的指令的,所以就不知道递归调用自己多少次。
- 而且如果这个函数代码量巨大,即使你写内联,编译器也不会内联处理,而且名字可能冲突了。
- 所以inline只是建议编译器把这个函数处理成内联函数!!!(是否inline成功,由编译器决定)
注意:
- 在debug版本上,inline是不起作用的,因为需要调试;inline只有在release版本下才能出现
反汇编代码:(表示vs2019上调试)
在Linux下查看是否内联函数:
- 在Linux下执行 g++ -c main.cpp -O2 (-c直接生成.o文件,不生成可执行文件)
objdump -t main.o(查看符号表 判断是否内联了,因为内联函数是没有符号的)