📖 学习信息
学习课程:黑马 C++ 零基础入门教程
本日学习:C++ 函数核心基础包含:函数的定义、函数的调用、值传递机制、函数的声明、函数的分文件编写(工业级规范)学习目标:掌握函数的基本使用方法,理解值传递的底层原理,学会用分文件编写规范组织代码,具备模块化编程的基础能力
一、前言
前面我们学习了变量、数据类型、运算符、流程控制和数组,当程序逻辑越来越复杂时,如果把所有代码都写在 main 函数里,会导致代码臃肿、难以阅读和维护。函数就是将一段重复使用的代码封装成一个独立的模块,通过函数名可以多次调用这段代码,极大提高代码的复用性和可维护性。函数是 C++ 程序的基本组成单元,任何复杂的 C++ 程序都是由无数个函数组成的,必须彻底掌握。
二、函数的定义
函数定义就是告诉编译器,这个函数叫什么名字、接收什么参数、返回什么值、具体要做什么事情。
1. 函数定义的语法
返回值类型 函数名(参数列表) {
// 函数体:具体执行的代码
return 返回值;
}
- 返回值类型:函数执行完毕后返回的数据类型,如果不需要返回值,写
void - 函数名:函数的标识符,遵循变量命名规范,建议见名知意
- 参数列表:函数接收的输入数据,多个参数用逗号分隔,没有参数可以留空
- 函数体:函数要执行的具体逻辑
- return 语句:结束函数并返回指定的值,返回值类型为
void时可以省略return
2. 简单示例:定义一个加法函数
#include <iostream>
using namespace std;
// 定义加法函数:接收两个int类型参数,返回它们的和
int add(int a, int b) {
int sum = a + b;
return sum;
}
int main() {
return 0;
}
三、函数的调用
函数定义完成后,不会自动执行,必须在 main 函数或其他函数中显式调用才会执行。
1. 函数调用的语法
函数名(实参列表);
- 实参列表:传递给函数的实际数据,个数和类型必须与函数定义的参数列表一致
2. 调用示例
#include <iostream>
using namespace std;
// 函数定义
int add(int a, int b) {
return a + b;
}
int main() {
// 调用add函数,传入实参10和20
int result = add(10, 20);
cout << "10 + 20 = " << result << endl; // 输出30
// 多次调用同一个函数
cout << "30 + 40 = " << add(30, 40) << endl; // 输出70
return 0;
}
3. 函数调用的执行流程
- 程序执行到函数调用语句时,暂停当前函数的执行
- 将实参的值传递给形参
- 跳转到被调用函数的函数体执行代码
- 执行到 return 语句或函数体结束时,返回到调用处继续执行后续代码
四、值传递(重点)
值传递是 C++ 中最基础的参数传递方式,也是函数调用时默认的传递方式。
1. 值传递的定义
值传递是指将实参的值拷贝一份传递给形参,形参和实参是两个独立的变量,存储在不同的内存空间中。
2. 核心特性
修改形参的值,不会影响实参的值。
3. 经典示例:尝试用值传递交换两个数
#include <iostream>
using namespace std;
// 尝试用值传递交换两个数
void swap(int a, int b) {
cout << "交换前:a = " << a << ",b = " << b << endl;
int temp = a;
a = b;
b = temp;
cout << "交换后:a = " << a << ",b = " << b << endl;
}
int main() {
int x = 10;
int y = 20;
cout << "调用函数前:x = " << x << ",y = " << y << endl;
swap(x, y);
cout << "调用函数后:x = " << x << ",y = " << y << endl;
return 0;
}
4. 运行结果与分析
调用函数前:x = 10,y = 20
交换前:a = 10,b = 20
交换后:a = 20,b = 10
调用函数后:x = 10,y = 20
分析:
- 实参 x 和 y 的值被拷贝给了形参 a 和 b
- 函数内部交换的是 a 和 b 的值,x 和 y 的值完全没有变化
- 这就是值传递的核心特点:形参是实参的副本,互不影响
五、函数的声明
如果函数定义在调用之后,编译器会报错,因为编译器是从上到下编译代码的,调用函数时还不知道这个函数的存在。这时候就需要函数声明,告诉编译器函数的名称、参数类型和返回值类型,让编译器能够通过编译,函数的具体定义可以放在后面。
1. 函数声明的语法
返回值类型 函数名(参数类型列表);
- 参数列表可以只写参数类型,不写参数名
- 函数声明末尾必须有分号
2. 示例:先声明后定义
#include <iostream>
using namespace std;
// 函数声明:告诉编译器有一个add函数
int add(int, int); // 可以省略参数名
int main() {
// 调用函数,此时编译器已经知道add函数的存在
int result = add(10, 20);
cout << result << endl;
return 0;
}
// 函数定义:放在调用之后
int add(int a, int b) {
return a + b;
}
3. 函数声明的注意事项
- 函数声明可以写多次,但函数定义只能写一次
- 函数声明的返回值类型、函数名、参数类型必须与定义完全一致
- 建议将函数声明写在程序开头,或者放在头文件中(分文件编写时会用到)
六、函数的分文件编写(工业级规范)
当程序越来越大,函数越来越多时,如果把所有函数都写在一个.cpp 文件里,会导致文件过于庞大,难以维护。分文件编写就是将函数的声明和定义分开,放在不同的文件中,是 C++ 工业级开发的标准规范。
1. 分文件编写的步骤
- 创建头文件(.h) :用于存放函数的声明
- 创建源文件(.cpp) :用于存放函数的定义
- 在使用函数的文件中,用
#include包含对应的头文件
2. 完整示例:分文件实现交换函数
第一步:创建头文件 swap.h
// swap.h:存放函数声明
#pragma once // 防止头文件被重复包含(必须写)
#include <iostream>
using namespace std;
// 函数声明
void swap(int a, int b);
第二步:创建源文件 swap.cpp
// swap.cpp:存放函数定义
#include "swap.h" // 包含对应的头文件
// 函数定义
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
cout << "交换后:a = " << a << ",b = " << b << endl;
}
第三步:创建主函数文件 main.cpp
// main.cpp:主函数所在文件
#include "swap.h" // 包含头文件,才能使用swap函数
int main() {
int x = 10;
int y = 20;
swap(x, y);
return 0;
}
3. 分文件编写的好处
- 代码结构清晰,每个文件只负责一个功能模块
- 便于团队协作开发,不同的人可以编写不同的文件
- 便于代码复用,其他项目可以直接引用头文件和源文件
- 提高编译效率,修改某个函数的定义时,只需要重新编译对应的源文件
七、常见错误与避坑点
-
函数没有返回值却写了 return 语句
// 错误:返回值类型是void,不能返回值 void func() { return 10; } -
有返回值的函数没有写 return 语句
// 错误:返回值类型是int,必须返回一个int值 int add(int a, int b) { int sum = a + b; // 缺少return sum; } -
值传递的误区误以为修改形参会影响实参,记住:值传递是拷贝,形参和实参是独立的
-
函数声明和定义不一致
// 声明:返回值是int int add(int a, int b); // 定义:返回值是double,与声明不一致,报错 double add(int a, int b) { return a + b; } -
分文件时忘记包含头文件主函数文件中必须用
#include "头文件名.h"包含对应的头文件,否则编译器找不到函数 -
头文件重复包含必须在头文件开头加上
#pragma once,防止头文件被多次包含导致重复定义错误
八、今日学习总结
-
函数的定义:
- 语法:
返回值类型 函数名(参数列表) { 函数体 return 返回值; } - 无返回值时返回值类型写
void,可以省略return
- 语法:
-
函数的调用:
- 语法:
函数名(实参列表); - 实参的个数和类型必须与形参一致
- 语法:
-
值传递:
- 本质:实参的值拷贝给形参
- 核心特性:修改形参不影响实参
-
函数的声明:
- 作用:告诉编译器函数的存在,让编译器通过编译
- 语法:
返回值类型 函数名(参数类型列表); - 声明可以写多次,定义只能写一次
-
分文件编写:
- 头文件(.h):存放函数声明,开头加
#pragma once - 源文件(.cpp):存放函数定义,包含对应的头文件
- 主函数文件:包含头文件,调用函数
- 头文件(.h):存放函数声明,开头加
✍️ 下节预告
下一篇将进入 C++ 核心难点 ——指针全套知识,包含:指针的定义与基础使用、指针占用内存空间、空指针、野指针、const 修饰指针、指针与数组结合、指针与函数组合运用,吃透指针原理,为后续面向对象、内存操作、复杂算法打好关键基础。