一、缺省参数
1、概念
缺省参数是声明或定义函数时为函数的参数指定一个缺省值。
在调用该函数的时候,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参!
#include<iostream>
using namespace std;
void Func(int a = 0)
{
cout << a << endl;
}
int main()
{
Func(); // 没有传参时,使用参数的默认值
Func(10); // 传参时,使用指定的实参
return 0;
}
2、 缺省参数分类
全缺省参数(给所有的参数指定一个值)
#include<iostream>
using namespace std;
void Func(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl<< endl;
}
int main()
{
Func();
Func(1);
Func(1,2);
Func(1, 2, 3);
return 0;
}
- 缺省参数的值从左到右依次赋予!
- 传参是从左往右依次传参!
- 中间不能隔着给值
应用示例:
struct Stack
{
int* a;
int top;
int capacity;
};
void StackInit(struct Stack* pst, int defaultCapacity = 4)
{
pst->a = (int*)malloc(sizeof(int) * defaultCapacity);
if (pst->a == NULL)
{
perror("malloc fail");
return;
}
pst->top = 0;
pst->capacity = defaultCapacity;
}
int main()
{
struct Stack pst;
StackInit(&pst,100);
return 0;
}
通过缺省参数我们可以默认给栈的初始化赋值为4,如果这个栈过大,存放的数据过多,会一直不断扩容造成效率低下,因此可以使用缺省参数的形式避免这种情况!
注意点:缺省参数不能在函数声明和定义中同时出现(且只能在函数的)
Stack.h文件
Stack.c文件
Test.c文件
C++在编译的时候将头文件展开,头文件里面只有函数的声明,没有函数的定义,在链接的时候两个函数才链接在一起,才有了函数的定义!
因此缺省参数不能在函数的定义和声明中同时出现,且只能在函数的声明中出现。否则编译的时候就报错了!
定义的时候不关心是不是缺省参数,因为总有两个参数传递过来,反而当有了声明的时候,在调用的时候如果只有一个参数,可以将其转化为两个参数!
二、函数重载
些同名函数的形参列表( 参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型
不同的问题。 (对返回值没有要求!)
函数名相同,参数相同,返回值不同,不构成重载!(编译不通过!)
#include<iostream>
using namespace std;
// 1、参数类型不同
int Add(int left, int right)
{
cout << "int Add(int left, int right)" << endl;
return left + right;
}
double Add(double left, double right)
{
cout << "double Add(double left, double right)" << endl;
return left + right;
}
// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
// 3、参数类型顺序不同
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
cout << "f(char b, int a)" << endl;
}
int main()
{
Add(10, 20);
Add(10.1, 20.2);
f();
f(10);
f(10, 'a');
f('a', 10);
return 0;
}
参数名不同不能构成重载!
特殊情况:
使用缺省参数能构成函数重载,但是,在执行f()的时候会出现歧义!不知道执行那一个!
注意点:自动识别类型本质上就是函数重载!
在链接的时候两个可执行程序才会交互在一起!
当我们在调用函数的时候,对应的汇编代码本质上是:call+对应的地址
call函数之后会跳到一个jump指令中去,(call是一个跳转指令,jump也是一个跳转指令!)
在编译阶段能否拿到函数的地址?
不能!编译阶段文件中只有函数的声明,没有函数的定义,在最后的链接的过程中才能在符号表中找到函数的地址,从而实现链接!
只有声明可以过(可以进行接下来的操作)-> 声明是一种承诺(可以在接下来的链接过程中找到对应的地址!)
链接:找到定义(兑现承诺)
在链接的过程中,符号表存放了函数的地址:
C语言中符号表的名字和汇编代码中调用的函数名直接就是函数的名字!因此,编译的时候就出错了!
gcc可能兼容c++;
objdump -S 可执行程序名
可以查看对应可执行程序的汇编代码
在Linux环境下我们可以查看生成的可执行程序的汇编代码:
我们发现汇编代码对应的函数名就是函数本身的函数名!
假设我们当前的C++程序如下所示:(有两个函数重载)
解下来查看对应的汇编的函数的代码:
- _Z是C++的规定;
- 4代表函数名func为4个字符;
- id表示int和double两个参数的类型
- _Z9是因为StackPush为9个字符;
- P为指针类型;
- 5是因为Stack为5个字符,Stack类型的指针;
- 类型一个为i,一个为d ;
因此,C++对应的汇编代码的函数名和符号表中的函数名应该如下黑色:
疑问:
是不是所有的函数都需要进行链接?
不是!如果一个函数的实现就位于test.cpp文件中,此时链接不需要在到其他的二进制代码文件中进行链接!
函数名修饰规则带入返回值,返回值不能能否构成重载?
不能!编译时期就会爆粗!(不知道要调用哪个函数,分不清头文件对应的函数的声明)
例如Func,此时会发生调用歧义,不知道调用的是哪个类型的函数!
如果规定返回值,此时相当于自己创造了一门语言!
符号表在链接阶段才会生成!
上述命名规则是在gcc/g++的使用规则,而在Windows情况下会有不一样的命名规则:
三、引用
java只有引用,没有指针!
取地址而言,都是用的同一块空间!
且b++和d++同时也会对a进行改变!
int& d;
上面这种写法是错误的,引用必须给出别名!
注意点:
这里是赋值,引用的指向不能发生改变!
引用的应用:例如交换两个数字的值:
a是x的别名;b是y的别名;
a和b的交换就是x和y的交换!
如果改变int需要用int*,如果改变int*需要用int**!
同理,我们也可以对指针进行引用,交换a和b指针实际上就是交换px和py!
链表使用引用:
上面的*PLTNode实际上就是struct ListNode* !