一、缺省参数(默认参数)
1. 什么是缺省参数?
缺省参数就是声明 / 定义函数时,为形参指定一个默认值。调用函数时,如果没传实参,就用这个默认值;传了实参,就用传入的值。 也叫 “默认参数”,分为全缺省(所有形参都给默认值)和半缺省(部分形参给默认值)。
2. 核心规则
半缺省参数必须从右往左连续给,不能间隔跳跃。
调用带缺省参数的函数时,实参必须从左到右依次给,不能跳跃传参。
函数声明和定义分离时,缺省参数不能同时在声明和定义中出现,必须只在声明里给。
示例
// 声明时给缺省值 void func(int a, int b = 10, int c = 20); int main() { func(1); // a=1, b=10, c=20 func(1, 2); // a=1, b=2, c=20 func(1,2,3);// a=1, b=2, c=3 return 0; } // 定义时不再写缺省值 void func(int a, int b, int c) { // ... }二、函数重载
1. 什么是函数重载?
C++ 支持在同一作用域中定义同名函数,但这些函数的形参必须满足:
参数个数不同,或
参数类型不同(或顺序不同)。
这种特性让函数调用表现出 “多态行为”,使用更灵活。C 语言不支持同一作用域的同名函数。
示例
int add(int a, int b) { return a + b; } double add(double a, double b) { return a + b; } int add(int a, int b, int c) { return a + b + c; } int main() { add(1, 2); // 调用int版 add(1.5, 2.5); // 调用double版 add(1, 2, 3); // 调用三参数版 return 0; }三、引用的概念与用法
1. 什么是引用?
引用不是定义新变量,而是给已存在的变量取一个别名。编译器不会为引用开辟额外内存空间,它和原变量共用同一块内存。
语法:类型& 引用别名 = 引用对象;
示例
int a = 10; int& b = a; // b是a的别名 cout << &a << endl; // 地址和a相同 cout << &b << endl;
2. 引用的四大功能
① 做函数形参:修改实参的值
// 错误:值传递,无法交换void swap(int p1, int p2) {
int tmp = p1;
p1 = p2;
p2 = tmp;
}
// 正确:引用传递,直接修改实参
void swap(int& p1, int& p2) {
int tmp = p1;
p1 = p2;
p2 = tmp;
}
② 做函数形参:减少拷贝,提高效率
当参数是大型结构体 / 类时,用引用可以避免整个对象拷贝,提升性能:
struct A { /* 大型数据 */ }; // 传值:拷贝整个A对象 void func(struct A aa) {} // 传引用:只传别名,不拷贝 void func(struct A& aa) {}③ 做返回值类型:修改返回的对象
传值返回会生成临时变量,无法被修改;传引用返回可以直接修改原对象:
// 传值返回:返回临时变量,无法被赋值 int SLAt(SL& sl, int i) { assert(i < sl.size); return sl.a[i]; } // 传引用返回:返回元素的引用,可以直接修改 int& SLAt(SL& sl, int i) { assert(i < sl.size); return sl.a[i]; } int main() { SL s; SLAt(s, 0) = 10; // 传引用返回时合法,传值返回会报错 return 0; }④ 做返回值类型:减少拷贝,提高效率
返回大型对象时,引用返回避免拷贝临时对象,提升性能。
3. 引用的安全问题:不能返回局部变量的引用
如果函数返回局部变量的引用,函数结束后局部变量的内存会被销毁,引用就会变成 “野引用”,导致未定义行为。
错误示例
int& func() { int ret = 0; // 局部变量 return ret; // 错误:返回局部变量的引用 } int main() { int& x = func(); cout << x << endl; // 内存已销毁,结果不可预测 return 0; }原因图解
func() 栈帧: ┌─────────────┐ │ ret = 0 │ ← 函数结束后,这块内存被销毁 └─────────────┘ main() 栈帧: ┌─────────────┐ │ x = &ret │ ← ret已销毁,x指向无效内存 └─────────────┘
四、指针引用的应用:链表头结点修改
当需要在函数中修改链表头指针时,用指针引用可以直接修改原指针,避免二级指针的复杂写法:
typedef struct ListNode { int val; struct ListNode* next; } LTNode, *PNode; // 用指针引用修改头结点 void ListPushBack(PNode& phead, int x) { PNode newnode = (PNode)malloc(sizeof(LTNode)); newnode->val = x; newnode->next = NULL; if (phead == NULL) { phead = newnode; // 直接修改原头指针 } else { // ... 找尾结点插入 } } int main() { PNode plist = NULL; ListPushBack(plist, 1); ListPushBack(plist, 2); return 0; }五、总结
特性 | 核心作用 |
缺省参数 | 简化函数调用,给参数提供默认值,注意声明和定义不能同时给缺省值 |
函数重载 | 同一作用域定义同名函数,通过形参区分,实现 “多态调用” |
引用 | 变量的别名,可用于修改实参、减少拷贝、修改返回对象,但不能返回局部变量引用 |