🔥 引言:在C++编程中,函数是实现代码模块化、提升复用性的核心载体。无论是初学者入门,还是开发者日常开发、面试复习,函数都是绕不开的重点。本文基于系统知识点梳理,从函数基础定义到指针、引用高级用法,搭配完整可复制的运行代码+常见坑点提醒,循序渐进拆解核心要点,适合C++新手夯实基础,也可作为进阶者的查漏补缺手册。建议收藏备用,跟着代码实操加深理解!
一、函数概述:程序的“功能积木”
C++程序的执行逻辑围绕函数展开,掌握以下3个核心特性,轻松入门:
程序由若干函数组成,有且仅有一个主函数(main函数)——主函数是程序唯一入口和出口,不能被其他函数调用。
子函数平行独立,可相互调用;执行流程:
main函数启动 → 调用子函数执行 → 返回main函数 → 程序在main中结束。函数分类:
按用户角度:标准函数(库函数,如
cout、sqrt,直接调用)、自定义函数(按需编写);按形式角度:无参数函数(无需传参)、有参数函数(需传指定数据)。
敲黑板:无论程序多复杂,最终都会拆解为一个个函数,模块化思维是编程的核心能力!
二、函数定义:3步写出规范函数
2.1 定义的一般形式
任何函数都遵循“类型说明+函数名+参数+函数体”的结构,通用语法:
类型说明 函数名([形式参数说明]) { // 函数体:局部变量声明 + 执行语句 }- 局部变量:仅在函数内部有效,函数执行结束后自动释放内存(比如在
ave函数中定义的answer,出了函数就失效)。
2.2 3种特殊函数定义(含可运行示例)
(1)无参函数:无需传参,执行固定操作
#include <iostream> using namespace std; // 无参函数:打印分隔线 void star() { cout << "\n****************\n"; } int main() { star(); // 调用无参函数 cout << "C++函数基础学习" << endl; star(); // 再次调用,复用代码 return 0; }✅ 运行结果:
**************** C++函数基础学习 ****************(2)有参函数:传参实现灵活逻辑(核心常用)
#include <iostream> using namespace std; // 有参函数:计算两个浮点数的平均值 float ave(float num1, float num2) { float answer = (num1 + num2) / 2; return answer; // 返回计算结果 } int main() { float a = 3.5, b = 6.5; float res = ave(a, b); // 传入实参,接收返回值 cout << "平均值:" << res << endl; // 输出:5.0 return 0; }(3)空函数:占位备用,便于后续扩展
// 空函数:仅声明结构,暂不实现功能 void func_reserve(int x) {} int main() { func_reserve(10); // 可正常调用,无实际操作 return 0; }避坑指南:空函数不是无用函数,在大型项目中用于预留功能位置,后续可直接补充函数体,不影响整体代码结构。
三、函数参数与返回值:数据传递的核心逻辑
3.1 形参 vs 实参(表格对比+避坑)
函数调用时的数据传递依赖参数,两者核心区别如下,避免混淆:
特性
形式参数(形参)
实际参数(实参)
定义位置
函数定义时的参数声明(如
ave中的num1)函数调用时传入的具体数据(如
ave(a, b)中的a)内存占用
函数调用时分配,执行结束后释放
自身作用域内持续占用(如
main中的a)合法类型
只能是变量或数组
常量、变量、表达式(如
ave(3.5, 6.5)、ave(a+1, b*2))核心原则:实参与形参必须“类型一致、个数匹配”,默认是值传递(形参修改不影响实参)。
示例:值传递的特性(形参修改不影响实参)
#include <iostream> using namespace std; int max(int x, int y) { x = 10; // 修改形参 y = 20; return x > y ? x : y; } int main() { int a = 3, b = 5; int c = max(a, b); // 传入a、b的副本 cout << "Max: " << c << endl; // 输出:20 cout << "a=" << a << ", b=" << b << endl; // 输出:a=3, b=5(实参未变) return 0; }
3.2 函数返回值的3个关键规则
有返回值的函数,必须用
return语句(可返回常量、变量、表达式),执行到return立即结束函数:int add(int x, int y) {if (x < 0) return 0; // 条件满足时直接返回return x + y;}返回值类型默认是
int型;若类型不一致,仅数值型(int/float/double)可自动转换(以函数声明类型为准):float sum() { return 5; } // 5是int型,自动转为float型返回无返回值的函数,用
void声明,不可引用其返回值:void print() { cout << "Hello"; }// 错误用法:int a = print(); // void函数不能返回值避坑指南:不要在
void函数中写return 1;(语法错误),若需提前结束函数,直接写return;即可。
四、函数调用:4种方式+执行流程
4.1 调用的基本形式
函数名(实参表列),无参函数必须保留括号(如star()),不可省略!
4.2 4种常见调用方式(附示例)
函数语句:仅执行操作,不接收返回值(适用于
void函数):star(); // 仅打印分隔线,无需接收返回值函数表达式:返回值参与运算:
int c = 3 * max(a, b); // max的返回值 ×3 赋值给c函数参数:一个函数的返回值作为另一个函数的实参(嵌套调用):
cout << max(a, max(b, c)) << endl; // 内层max返回值作为外层实参数组名/指针/引用传参:后续重点讲解,实现双向数据传递。
4.3 函数调用的执行流程(图解)
1. 计算实参值 → 2. 实参值传递给形参 → 3. 执行被调函数体 → 4. 遇return返回主调函数(带返回值)
4.4 函数原形说明:解决“先调用后定义”问题
C++要求“先定义后使用”,若函数定义在
main之后,必须提前声明原形(告知编译器函数信息):- 原形格式:
类型说明 函数名(参数类型1, 参数类型2, ...);(参数名可省略)示例:先声明后定义(实际开发常用)
#include <iostream> using namespace std; int main() { float add(float, float); // 函数原形声明(参数名可省) float a = 2.5, b = 3.5, c; c = add(a, b); // 调用时编译器已知道函数信息 cout << "sum: " << c << endl; // 输出:6.0 return 0; } // 函数定义在main之后 float add(float x, float y) { return x + y; }避坑指南:原形声明时,参数类型必须与定义一致,否则会出现“函数不匹配”编译错误!
五、数组作为函数参数:批量数据传递技巧
数组作为批量数据载体,传参有2种核心形式,重点区分“值传递”和“地址传递”:
5.1 数组元素作为实参(值传递)
数组元素本质是普通变量,传参规则与普通变量一致,形参修改不影响原数组:
#include <iostream> using namespace std; int max(int x, int y) { return x > y ? x : y; } int main() { int a[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10}; int m = a[0]; for (int i = 1; i < 10; i++) { m = max(m, a[i]); // 数组元素a[i]作为实参(值传递) } cout << "Max: " << m << endl; // 输出:10 return 0; }
5.2 数组名作为实参(地址传递,核心常用)
数组名本质是“数组首元素地址”,传参时传递的是地址,形参和实参共享同一块内存——修改形参会直接改变原数组!
示例:数组名传参求最大值
#include <iostream> using namespace std; // 形参x[]接收首地址,n接收元素个数(必须传!) int max(int x[], int n) { int m = x[0]; for (int i = 1; i < n; i++) { if (m < x[i]) m = x[i]; } return m; } int main() { int a[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10}; int c = max(a, 10); // 数组名a=首地址,10是元素个数 cout << "Max: " << c << endl; // 输出:10 return 0; }避坑指南:数组名传参时,形参
x[]可省略大小(如int x[]),但必须额外传递元素个数n——编译器无法通过x[]获取数组长度!
5.3 多维数组作为参数(关键规则)
多维数组传参时,第一维大小可省略,其他维必须明确指定(编译器需计算内存偏移):
- 合法格式:
int func(int x[3][5])、int func(int x[][5])- 非法格式:
int func(int x[3][])、int func(int x[][])(编译报错)示例:二维数组传参求每行最大值
#include <iostream> using namespace std; // 二维数组传参:第二维大小必须指定为5 void rowMax(int x[][5], int row) { for (int i = 0; i < row; i++) { int m = x[i][0]; for (int j = 1; j < 5; j++) { if (m < x[i][j]) m = x[i][j]; } cout << "第" << i+1 << "行最大值:" << m << endl; } } int main() { int arr[3][5] = {{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}}; rowMax(arr, 3); // 传递数组名+行数 return 0; }
六、指针与函数(高级用法):突破值传递限制
指针直接操作内存地址,与函数结合可实现“双向数据传递”,是C++进阶的核心考点!
6.1 指针变量作为函数参数(修改实参)
通过传递变量地址,形参指针解引用(
*x)可直接修改实参的值,解决值传递的单向限制:示例:指针实现两数交换(面试高频题)
#include <iostream> using namespace std; void swap(int *x, int *y) { // 形参为int*指针(接收地址) int temp = *x; // *x = 实参a的值 *x = *y; // 修改实参a的值 *y = temp; // 修改实参b的值 } int main() { int a = 3, b = 5; swap(&a, &b); // 传递a、b的地址(&是取地址符) cout << "a=" << a << ", b=" << b << endl; // 输出:a=5, b=3 return 0; }避坑指南:指针传参时,确保指针指向有效内存(避免空指针
NULL),否则会触发内存访问错误!
6.2 数组指针作为函数参数(等价于数组名传参)
数组名=首地址,数组指针传参本质也是地址传递,通过指针偏移(
*(p+i))访问数组元素:#include <iostream> using namespace std; // _p为数组指针,n为元素个数 int maxArray(int *_p, int n) { int maxVal = *_p; // *_p = a[0] for (int i = 1; i < n; i++) { if (*(_p + i) > maxVal) { // *(p+i) = a[i] maxVal = *(_p + i); } } return maxVal; } int main() { int a[10] = {23, 43, 52, 96, 34}; int max = maxArray(a, 5); // 数组名=指针实参 cout << "Max: " << max << endl; // 输出:96 return 0; }
6.3 指向函数的指针(动态切换函数调用)
函数也有内存地址,用“函数指针”可存储该地址,动态调用不同函数,提升代码灵活性(进阶考点):
示例:函数指针实现和、差、积的动态调用
#include <iostream> using namespace std; int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; } int mul(int x, int y) { return x * y; } int main() { int a = 10, b = 5; // 定义函数指针:指向“返回int、参数为两个int”的函数 int (*pFunc)(int, int); pFunc = add; // 指针指向add函数 cout << "10+5=" << pFunc(a, b) << endl; // 输出:15 pFunc = sub; // 指针指向sub函数 cout << "10-5=" << pFunc(a, b) << endl; // 输出:5 pFunc = mul; // 指针指向mul函数 cout << "10*5=" << pFunc(a, b) << endl; // 输出:50 return 0; }应用场景:在菜单功能、回调函数中常用,比如根据用户选择动态调用不同功能函数。
七、引用与函数:更简洁的地址传递
引用是变量的“别名”,本质是指针的封装,语法更简洁,无需解引用,是实际开发中修改实参的首选方式!
示例:引用实现两数交换(对比指针更简洁)
#include <iostream> using namespace std; // 形参为int&引用(a、b是实参的别名) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } int main() { int i = 3, j = 5; swap(i, j); // 直接传变量名,无需取地址 cout << "i=" << i << ", j=" << j << endl; // 输出:i=5, j=3 return 0; }引用 vs 指针(核心区别,面试必问)
特性
引用(&)
指针(*)
语法复杂度
简洁,无需解引用/取地址
复杂,需解引用(*)、取地址(&)
空值情况
无空引用,必须初始化
可有空指针(NULL)
指向修改
一旦绑定,不可更改指向
可动态切换指向的变量
安全性
高,避免空指针问题
低,需手动检查空指针
开发建议:若仅需修改实参,优先用引用(简洁安全);若需动态切换指向或处理空值,用指针。
八、核心总结+学习建议+进阶方向
8.1 核心知识点速记
函数是模块化核心,
main函数是唯一入口,子函数平行独立;参数传递分2类:值传递(单向)、地址传递(双向,指针/数组名/引用);
数组传参=地址传递,多维数组需明确除第一维外的所有维度;
引用简洁安全,指针灵活强大,按需选择。
8.2 学习建议
代码实操:将本文所有代码复制到编译器(Dev-C++/VS Code),逐行调试,观察变量变化;
避坑训练:刻意练习“数组传参忘传元素个数”“指针空引用”等错误,理解报错原因;
实战巩固:用函数实现数组排序(冒泡/选择)、字符串反转、素数判断等功能。
8.3 进阶方向
掌握本文基础后,可进一步学习:
函数重载(同名函数不同参数);
递归函数(函数调用自身,如斐波那契数列);
内联函数、函数模板(泛型编程);
类成员函数(面向对象编程)。