C++ 系统学习日记・第 08 天|函数基础:定义 + 调用 + 值传递 + 声明 + 分文件编写

2 阅读8分钟

📖 学习信息

学习课程:黑马 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. 函数调用的执行流程

  1. 程序执行到函数调用语句时,暂停当前函数的执行
  2. 将实参的值传递给形参
  3. 跳转到被调用函数的函数体执行代码
  4. 执行到 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. 函数声明的注意事项

  1. 函数声明可以写多次,但函数定义只能写一次
  2. 函数声明的返回值类型、函数名、参数类型必须与定义完全一致
  3. 建议将函数声明写在程序开头,或者放在头文件中(分文件编写时会用到)

六、函数的分文件编写(工业级规范)

当程序越来越大,函数越来越多时,如果把所有函数都写在一个.cpp 文件里,会导致文件过于庞大,难以维护。分文件编写就是将函数的声明和定义分开,放在不同的文件中,是 C++ 工业级开发的标准规范。

1. 分文件编写的步骤

  1. 创建头文件(.h) :用于存放函数的声明
  2. 创建源文件(.cpp) :用于存放函数的定义
  3. 在使用函数的文件中,用#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. 分文件编写的好处

  1. 代码结构清晰,每个文件只负责一个功能模块
  2. 便于团队协作开发,不同的人可以编写不同的文件
  3. 便于代码复用,其他项目可以直接引用头文件和源文件
  4. 提高编译效率,修改某个函数的定义时,只需要重新编译对应的源文件

七、常见错误与避坑点

  1. 函数没有返回值却写了 return 语句

    // 错误:返回值类型是void,不能返回值
    void func() {
        return 10;
    }
    
  2. 有返回值的函数没有写 return 语句

    // 错误:返回值类型是int,必须返回一个intint add(int a, int b) {
        int sum = a + b;
        // 缺少return sum;
    }
    
  3. 值传递的误区误以为修改形参会影响实参,记住:值传递是拷贝,形参和实参是独立的

  4. 函数声明和定义不一致

    // 声明:返回值是int
    int add(int a, int b);
    
    // 定义:返回值是double,与声明不一致,报错
    double add(int a, int b) {
        return a + b;
    }
    
  5. 分文件时忘记包含头文件主函数文件中必须用#include "头文件名.h"包含对应的头文件,否则编译器找不到函数

  6. 头文件重复包含必须在头文件开头加上#pragma once,防止头文件被多次包含导致重复定义错误


八、今日学习总结

  1. 函数的定义

    • 语法:返回值类型 函数名(参数列表) { 函数体 return 返回值; }
    • 无返回值时返回值类型写void,可以省略return
  2. 函数的调用

    • 语法:函数名(实参列表);
    • 实参的个数和类型必须与形参一致
  3. 值传递

    • 本质:实参的值拷贝给形参
    • 核心特性:修改形参不影响实参
  4. 函数的声明

    • 作用:告诉编译器函数的存在,让编译器通过编译
    • 语法:返回值类型 函数名(参数类型列表);
    • 声明可以写多次,定义只能写一次
  5. 分文件编写

    • 头文件(.h):存放函数声明,开头加#pragma once
    • 源文件(.cpp):存放函数定义,包含对应的头文件
    • 主函数文件:包含头文件,调用函数

✍️ 下节预告

下一篇将进入 C++ 核心难点 ——指针全套知识,包含:指针的定义与基础使用、指针占用内存空间、空指针、野指针、const 修饰指针、指针与数组结合、指针与函数组合运用,吃透指针原理,为后续面向对象、内存操作、复杂算法打好关键基础。