news 2026/4/18 0:30:53

C++入门讲解4:函数完全攻略从基础到进阶(含可运行代码+避坑指南)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++入门讲解4:函数完全攻略从基础到进阶(含可运行代码+避坑指南)

🔥 引言:在C++编程中,函数是实现代码模块化、提升复用性的核心载体。无论是初学者入门,还是开发者日常开发、面试复习,函数都是绕不开的重点。本文基于系统知识点梳理,从函数基础定义到指针、引用高级用法,搭配完整可复制的运行代码+常见坑点提醒,循序渐进拆解核心要点,适合C++新手夯实基础,也可作为进阶者的查漏补缺手册。建议收藏备用,跟着代码实操加深理解!

一、函数概述:程序的“功能积木”

C++程序的执行逻辑围绕函数展开,掌握以下3个核心特性,轻松入门:

  1. 程序由若干函数组成,有且仅有一个主函数(main函数)——主函数是程序唯一入口和出口,不能被其他函数调用。

  2. 子函数平行独立,可相互调用;执行流程:main函数启动 → 调用子函数执行 → 返回main函数 → 程序在main中结束

  3. 函数分类:

    1. 按用户角度:标准函数(库函数,如coutsqrt,直接调用)、自定义函数(按需编写);

    2. 按形式角度:无参数函数(无需传参)、有参数函数(需传指定数据)。

敲黑板:无论程序多复杂,最终都会拆解为一个个函数,模块化思维是编程的核心能力!

二、函数定义: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个关键规则

  1. 有返回值的函数,必须用return语句(可返回常量、变量、表达式),执行到return立即结束函数:int add(int x, int y) {if (x < 0) return 0; // 条件满足时直接返回return x + y;}

  2. 返回值类型默认是int型;若类型不一致,仅数值型(int/float/double)可自动转换(以函数声明类型为准):float sum() { return 5; } // 5是int型,自动转为float型返回

  3. 无返回值的函数,用void声明,不可引用其返回值:void print() { cout << "Hello"; }// 错误用法:int a = print(); // void函数不能返回值

避坑指南:不要在void函数中写return 1;(语法错误),若需提前结束函数,直接写return;即可。

四、函数调用:4种方式+执行流程

4.1 调用的基本形式

函数名(实参表列),无参函数必须保留括号(如star()),不可省略!

4.2 4种常见调用方式(附示例)

  1. 函数语句:仅执行操作,不接收返回值(适用于void函数):star(); // 仅打印分隔线,无需接收返回值

  2. 函数表达式:返回值参与运算:int c = 3 * max(a, b); // max的返回值 ×3 赋值给c

  3. 函数参数:一个函数的返回值作为另一个函数的实参(嵌套调用):cout << max(a, max(b, c)) << endl; // 内层max返回值作为外层实参

  4. 数组名/指针/引用传参:后续重点讲解,实现双向数据传递。

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 核心知识点速记

  1. 函数是模块化核心,main函数是唯一入口,子函数平行独立;

  2. 参数传递分2类:值传递(单向)、地址传递(双向,指针/数组名/引用);

  3. 数组传参=地址传递,多维数组需明确除第一维外的所有维度;

  4. 引用简洁安全,指针灵活强大,按需选择。

8.2 学习建议

  1. 代码实操:将本文所有代码复制到编译器(Dev-C++/VS Code),逐行调试,观察变量变化;

  2. 避坑训练:刻意练习“数组传参忘传元素个数”“指针空引用”等错误,理解报错原因;

  3. 实战巩固:用函数实现数组排序(冒泡/选择)、字符串反转、素数判断等功能。

8.3 进阶方向

掌握本文基础后,可进一步学习:

  • 函数重载(同名函数不同参数);

  • 递归函数(函数调用自身,如斐波那契数列);

  • 内联函数、函数模板(泛型编程);

  • 类成员函数(面向对象编程)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 0:28:35

企业并购尽职调查:用anything-llm快速审阅大量文件

企业并购尽职调查&#xff1a;用anything-LLM快速审阅大量文件 在一场典型的并购交易中&#xff0c;买方团队常常面对堆积如山的PDF合同、密密麻麻的财务报表和数百封法律函件。一位资深律师曾苦笑&#xff1a;“我们不是在做决策&#xff0c;而是在做文献综述。”这正是传统尽…

作者头像 李华
网站建设 2026/4/17 0:44:40

WindowResizer深度解析:3个技巧彻底解决窗口调整难题

WindowResizer深度解析&#xff1a;3个技巧彻底解决窗口调整难题 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 你是否曾经为某些顽固的应用程序窗口而烦恼&#xff1f;那些无法随…

作者头像 李华
网站建设 2026/4/13 13:08:43

JoyCon-Driver终极指南:快速上手PC游戏新利器

JoyCon-Driver终极指南&#xff1a;快速上手PC游戏新利器 【免费下载链接】JoyCon-Driver A vJoy feeder for the Nintendo Switch JoyCons and Pro Controller 项目地址: https://gitcode.com/gh_mirrors/jo/JoyCon-Driver 还在为PC游戏找不到合适的手柄而烦恼吗&#…

作者头像 李华
网站建设 2026/4/13 11:55:20

7大核心功能揭秘:HunterPie如何彻底改变你的怪物猎人游戏体验

7大核心功能揭秘&#xff1a;HunterPie如何彻底改变你的怪物猎人游戏体验 【免费下载链接】HunterPie-legacy A complete, modern and clean overlay with Discord Rich Presence integration for Monster Hunter: World. 项目地址: https://gitcode.com/gh_mirrors/hu/Hunte…

作者头像 李华
网站建设 2026/4/13 1:07:02

如何用iverilog验证组合逻辑电路——实战案例

如何用iverilog验证组合逻辑电路——从零开始的实战指南你有没有过这样的经历&#xff1a;写完一个Verilog模块&#xff0c;心里总觉得“应该是对的”&#xff0c;但一上板就出问题&#xff1f;尤其是像多路选择器、加法器这类看似简单的组合逻辑&#xff0c;一旦输入组合复杂起…

作者头像 李华
网站建设 2026/4/16 12:29:58

终极RPG Maker插件宝典:解锁专业级游戏开发能力

RPG Maker插件集合为游戏开发者提供了强大的功能扩展&#xff0c;包含300多个精心设计的插件&#xff0c;全面覆盖游戏开发的各个环节。这些插件采用MIT开源协议&#xff0c;无论是个人学习还是商业项目都可以自由使用。 【免费下载链接】RPGMakerMV RPGツクールMV、MZで動作す…

作者头像 李华