三.函数的升级(上)
内联函数
C++中推荐使用内联函数替代宏代码片段
C++中使用inline关键字声明内联函数
注:内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求(例如和声明在一起时,会无效)
C++编译器可以将一个函数进行内联编译
被C++编译器内联编译的函数叫做内联函数
内联函数在最终生成的代码中是没有定义的
C++编译器直接将函数体插入函数调用的地方
内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
C++编译器不一定准许函数的内联请求!!
内联函数:1.内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型)
2.内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求
3.内联函数是由编译器处理,直接将编译后的函数插入调用的地方
宏代码片段由预处理器处理,进行简单的文本替换,没有任何编译过程,内联函数相对于宏代码片段,副作用更小
#include <stdio.h>
#define FUNC(a, b) ((a) < (b) ? (a) : (b))
inline int func(int a, int b)
{
return a < b ? a : b;
}
int main(int argc, char *argv[])
{
int a = 1;
int b = 3;
int c = func(++a, b);
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
这段代码,使用宏代码就会副作用
4.现代编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译
5.一些现代编译器提供了扩展语法,能够对函数进行强制内联,如: _attribute_((always_inline))属性
C++中内联编译的限制:
不能存在任何形式的循环语句
不能存在过多的条件判断语句
函数体不能过于庞大
不能对函数进行取址操作
函数内联声明必须在调用语句之前
编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。
因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
C++中内联函数的实现机制:将内联函数func存入符号表,用符号表中的函数体替换func调用
函数默认参数
C++中可以在函数声明时,为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译会自动用默认值代替
#include <stdio.h>
int mul(int x = 3);//声明时制定默认参数值
int main(int argc, char *argv[])
{
printf("mul(2) = %d\n", mul(2));//4
printf("mul(-2) = %d\n", mul(-2));//4
printf("mul() = %d\n", mul());//9
printf("Press enter to continue ...");
getchar();
return 0;
}
int mul(int x)//定义时不再指定
{
return x * x;
}
函数默认参数的规则
只有参数列表后面部分的参数才可以提供默认参数值
. 如果一个函数中有多个默认参数,则默认参数应从右至左逐渐定义。当调用函数时,只能从左向右匹配参数
void func(int a=1,int b,int c=3, int d=4); //error
void func(int a, int b=2,int c=3,int d=4); //ok
#include <stdio.h>
int add(int a, int b = 0, int c = 0, int d = 0)
{
return a + b + c;
}
int main(int argc, char *argv[])
{
printf("add(2) = %d\n", add(2));//2
printf("add(1, 2) = %d\n", add(1, 2));//3
printf("add(1, 2, 3) = %d\n", add(1, 2, 3));//6
printf("Press enter to continue ...");
getchar();
return 0;
}
int a=1;
int fun(int);
int g(int x;fun(a)); //ok:允许默认值为函数
默认值不可以是局部变量,因为默认参数的函数调用是在编译时确定的,而局部变量的位置与值在编译时均无法确定。例如:
void fun()
{
int i;
void g(int x=i); //error:处理g()函数声明时,i不可见
}
函数占位参数
#include <stdio.h>
int func(int a, int b, int)
{
return a + b;
}
int main(int argc, char *argv[])
{
printf("func(1, 2, 3) = %d\n", func(1, 2, 3));//必须提供三个实参,打印3
printf("Press enter to continue ...");
getchar();
return 0;
}
#include <stdio.h>
int func(int a, int b, int = 0)
{
return a + b;
}
int main(int argc, char *argv[])
{
printf("func(1, 2) = %d\n", func(1, 2));
printf("Press enter to continue ...");
getchar();
return 0;
}
函数的升级(下)
#include <stdio.h>
#include <string.h>
int func(int x)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
int func(int a, const char* s)
{
return a;
}
int func(const char* s, int a)
{
return strlen(s);
}
int main(int argc, char *argv[])
{
int c = 0;
c = func("ab", 1);
printf("c = %d\n", c);//c = 2
printf("Press enter to continue ...");
getchar();
return 0;
}
#include <stdio.h>
#include <string.h>
int func(int a, int b, int c = 0)
{
return a * b * c;
}
int func(int a, int b)
{
return a + b;
}
int main(int argc, char *argv[])
{
int c = 0;
c = func(1, 2); //存在二义性,调用失败,编译不能通过
printf("c = %d\n", c);
printf("Press enter to continue ...");
getchar();
return 0;
}
编译器调用重载函数的准则
以下三个函数能够成重载
int biggest(int a,int b,int c);
{
......
}
float biggest(float a,float b)
{
......
}
float biggest(float a,float b,float c)
{
......
}
以下不能重载
int biggest(int a,int b);
{
......
}
float biggest(int a,int b)
{
......
}
函数重载,只看参数个数,类型和顺序,不看返回值。重载的函数,虽然函数名相同, 但是编译会根据参数将其编译成不同的函数名,比如上面可能会编译成biggest_int_int_int, biggest_float_float, biggest_float_float_float,因此,只要参数不同,是可以的
#include <stdio.h>
#include <string.h>
int func(int x) // int(int a)
{
return x;
}
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
typedef int(*PFUNC)(int a); // int(int a)
int main(int argc, char *argv[])
{
int c = 0;
PFUNC p = func;
c = p(1);
printf("c = %d\n", c);//c = 1
printf("Press enter to continue ...");
getchar();
return 0;
}
C++和C的相互调用
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
注意:C++编译器不能以C的方式编译个多重载函数
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
int func(int a, int b)
{
return a + b;
}
int func(const char* s)
{
return strlen(s);
}
#ifdef __cplusplus
}
#endif
int main(int argc, char *argv[])
{
printf("Press enter to continue ...");
getchar();
return 0;
}
注:
所以在C++编译器下面 仍然是 C++的行为不会有所改变(例如const常量依旧会放入符号表)
标识符的编译方式是指,func(int a, int b)这个函数,在C编译器里面,可能会被编译成_func()
在C++编译器里面,会被编译成_func_int_int,加了extern 后就会编译成_func()C语言的编译器连接器就能识别了
C++就是靠这个方式来区别重载函数的,所以如果用了C的方式,func(int a, int b)和func(char ,int)都是命名成_func,不能区分了,所以不能用
不会干涉编译器的编译行为
所以在C++编译器下面 仍然是C++的行为不会有所改变
标识符的编译方式是指,func(int a, int b)这个函数,在C编译器里面,可能会被编译成_func()
在C++编译器里面,会被编译成_func_int_int
C++就是靠这个方式来区别重载函数的,所以如果用了C的方式,func(int a, int b)和func(char ,int)都是命名成_func,不能区分了,所以不能用