C++中的函数

C++中的函数

C++中的函数

C++中的函数:编写清晰且可复用代码的关键

你是否注意到,随着程序规模的增长,代码变得越来越难以理解和维护?如果你曾经觉得自己的代码像一个复杂的迷宫,那可能是因为你还没有充分利用C++的函数。函数就像构造模块,允许将程序划分为可管理的部分,使代码更易读、更易维护,并且更优化。在本课程中,你将学习如何有效地使用函数,以改善代码的组织结构,编写更结构化的程序,并使你的C++开发更加专业和高效。

学习目标

完成本课程后,你将学会:

  • 理解 函数的目的以及它们在C++中的重要性。
  • 创建 正确的函数,确保代码结构清晰。
  • 调用 函数并理解它们的执行方式。
  • 区分 返回值函数与仅执行指令的函数。
  • 比较 不同的函数定义方式,并根据实际情况选择最佳方案。

目录
函数的声明、调用和定义
方法一:先声明 – 调用 – 定义
方法二:声明后先实现再调用
返回值的传递
递归:调用自身的函数
函数的多返回值
函数重载(overloading)
C++中的内联函数(inline functions)
关于C++函数的最终思考

函数的声明、调用和定义

在C++中,函数是可重复使用的代码块,使程序结构更加模块化和有组织。每个函数封装一个特定的任务,有助于提高代码的清晰度和可维护性。要在程序中使用函数,我们需要遵循三个基本步骤:声明、调用和定义。

这三个概念至关重要,每个概念在代码结构中都扮演着特定的角色。让我们详细了解每个部分。

  1. 函数的声明

    在代码中使用函数之前,编译器必须知道它的存在。这是通过函数声明或函数原型实现的。

    函数声明向编译器提供三个关键信息:

    • 函数返回的数据类型(如果没有返回值,则使用void)。
    • 函数的名称。
    • 函数的参数列表(如果有),以及它们的数据类型。

    函数声明的一般语法如下:

    fan_hui_lei_xing han_shu_ming (can_shu_lie_biao);
    

    通常,函数声明放在main()之前,或者在多文件程序中放入头文件(.h)。

  2. 函数的调用

    声明函数后,我们可以在代码中调用它,使其执行定义的功能。

    调用函数时:

    • 执行其定义中包含的代码。
    • 如果函数有返回值,该值可以存储在变量中或直接用于表达式。
    • 如果函数是void类型,它只执行指令,不返回任何值。

    函数调用的语法很简单,只需写出函数名,并在括号内提供参数(如果需要):

    han_shu_ming(can_shu);
    
  3. 函数的定义

    函数定义是实现其功能的地方。这里指定了在调用函数时执行的指令。

    函数定义的一般语法如下:

    fan_hui_lei_xing han_shu_ming (can_shu_lie_biao) {
        // han_shu_ti: yao_zhi_xing_de_zhi_ling
        return zhi; // (ru_guo_han_shu_you_fan_hui_zhi)
    }
    

    每个函数定义必须遵循以下规则:

    • 如果函数已声明,则定义必须与声明匹配。
    • 如果函数返回一个值(例如int),必须包含return语句,并返回一个值。
    • 如果函数没有返回值(void),它只需执行代码块内的指令,无需return

执行流程

当程序运行时,函数按照它们在main()中的调用顺序执行。执行流程如下:

  1. 编译器识别函数声明。
  2. main()中,遇到函数调用时,程序控制流跳转到函数定义。
  3. 执行函数中的指令。
  4. 如果函数有返回值,该值会返回到调用函数的代码位置。
  5. 程序流程返回到main()或调用它的函数。

提前声明的重要性

在使用函数之前声明它是至关重要的,因为C++编译器按自上而下的顺序解析代码。如果在定义或声明之前调用函数,会导致错误。

主要有两种方式解决这个问题:

  1. main()之前声明函数,并在之后定义它(如前面介绍的方式)。
  2. main()之前定义函数,避免提前声明。

这两种方法都可以,但第一种方法在大型程序中更有用,尤其是涉及多个文件时。

方法:声明 – 调用 – 定义

在C++中,组织函数的一种常见方法是声明 – 调用 – 定义。 按照这种逻辑,我们可以将代码分为三个基本阶段:

  1. 声明: 在使用函数之前,向编译器声明其存在,指定函数名、返回类型及参数(如果有)。
  2. 调用: 在代码主函数(通常是main())中调用函数,执行其功能。
  3. 定义: 详细实现函数,指定当函数被调用时执行的指令。

这种结构有助于提高代码的组织性,使其更易维护和扩展。让我们来看一个示例,该示例采用这种方法:

示例:函数 consoladice()

在下面的代码中,我们遵循声明 – 调用 – 定义的顺序:

#include <iostream>
using namespace std;
// shou xian sheng ming han shu
void consoladice();
int main() {
    // diao yong han shu
    consoladice();
    return 0;
}
// ding yi xian qian sheng ming de han shu, xiang xi qi nei bu shi xian
void consoladice() {
    cout << "zhe shi yi tiao jian dan de wen ben huo zifu chuan." << endl;
    cout << "xian zai wo gei ni kan yi ge shu zi wu. zhe li shi: " << 5 << endl;
    cout << "kan kan 10/5 de jie guo shi shen me. jie guo shi: " << 10/5 << endl;
    cout << "yi zhong chang jian de pi de jin si shi 22/7. jie guo shi: " << 22/7 << endl;
    cout << "zai C++ zhong, 22/7 he 22.0/7 bing bu yi yang." << endl;
    cout << "zhi yao zuo zhe yang de xiao bian hua, wo men ke yi kan dao 22.0/7 de jie guo shi " << 22.0/7 << endl;
    cout << "ni bu jue de zhe yang de jie guo geng hao ma?" << endl;
}

该代码的预期输出为:

zhe shi yi tiao jian dan de wen ben huo zifu chuan.
xian zai wo gei ni kan yi ge shu zi wu. zhe li shi: 5
kan kan 10/5 de jie guo shi shen me. jie guo shi: 2
yi zhong chang jian de pi de jin si shi 22/7. jie guo shi: 3
zai C++ zhong, 22/7 he 22.0/7 bing bu yi yang.
zhi yao zuo zhe yang de xiao bian hua, wo men ke yi kan dao 22.0/7 de jie guo shi 3.14286
ni bu jue de zhe yang de jie guo geng hao ma?

为了更好地理解“声明 – 调用 – 定义”方法,让我们关注代码的三个关键部分:

  1. 第5行:函数声明
    • void consoladice(); 告诉编译器代码中存在一个名为consoladice()的函数。
    • 其返回类型为void,表示该函数不返回任何值。
    • 尽管此时尚未实现consoladice()的功能,但这个声明使编译器在之后的代码中识别该函数。
  2. 第9行:函数调用
    • main()函数内部,语句 consoladice(); 触发该函数的执行。
    • 由于之前的声明,编译器已知道consoladice()的存在。
    • 当调用该函数时,程序控制流跳转到其定义处,并执行其内容。
  3. 第14至22行:函数定义
    • 在这里,consoladice() 的实现被详细定义。
    • 该函数在控制台上输出多个信息,包括文本、数字和数学计算结果。
    • 值得注意的是 22/722.0/7 之间的区别。当使用 22/7 时,两个数都是整数,因此执行的是整数除法,结果为 3。但当使用 22.0/7 时,运算会自动转换为浮点运算,结果为 3.14286

方法:先声明并实现,再调用

在C++中,除了声明 – 调用 – 定义的方法外,还有另一种组织函数的方式:先声明并实现,再调用。 这种方法在定义函数时,不需要单独的声明,而是在main()之前直接定义函数。

在此方法中,我们不需要先声明函数再在main()后面定义,而是直接在main()之前声明并实现函数。这种方式的优点是代码更加紧凑,在小型程序中更容易阅读。

此方法的一般结构如下:

// zai main() qian ding yi han shu
fan_hui_lei_xing han_shu_ming(can_shu_lie_biao) {
    // han_shu_ti
    return zhi; // ru_guo_xu_yao
}
int main() {
    // diao yong han shu
    han_shu_ming(can_shu);
}

由于函数在main()之前已定义,编译器在调用它时已经知道其存在,因此无需单独声明。

示例:不需要单独声明的 consoladice() 函数

现在来看一个实际示例,应用这种方法:

#include <iostream>
using namespace std;
// zai diao yong zhi qian ding yi han shu
void consoladice() {
    cout << "zhe shi yi tiao jian dan de wen ben huo zifu chuan." << endl;
    cout << "xian zai wo gei ni kan yi ge shu zi wu. zhe li shi: " << 5 << endl;
    cout << "kan kan 10/5 de jie guo shi shen me. jie guo shi: " << 10/5 << endl;
    cout << "yi zhong chang jian de pi de jin si shi 22/7. jie guo shi: " << 22/7 << endl;
    cout << "zai C++ zhong, 22/7 he 22.0/7 bing bu yi yang." << endl;
    cout << "zhi yao zuo zhe yang de xiao bian hua, wo men ke yi kan dao 22.0/7 de jie guo shi " << 22.0/7 << endl;
    cout << "ni bu jue de zhe yang de jie guo geng hao ma?" << endl;
}
int main() {
    // diao yong han shu
    consoladice();
    return 0;
}

在该代码中,我们可以看到:

  1. 在第5至13行,函数的声明和定义合并

    consoladice() 直接在main()之前定义,无需额外声明。

  2. 在第17行,函数在main()内被调用

    由于该函数已被定义,编译器能正确解析并执行它。

  3. 运行结果完全相同

    与“声明 – 调用 – 定义”方法相比,该方法的执行结果完全相同,但代码结构更加紧凑。

✅ 优势:

  • 在小型程序中,代码更加简洁明了。
  • 无需额外声明,减少代码行数。
  • 在所有函数都位于同一文件的小型脚本中,便于阅读。

❌ 劣势:

  • 在大型程序中,如果main()之前定义了大量函数,可能会影响代码的组织性。
  • 如果使用多个文件(.h 和 .cpp),这种方法不太适用,通常建议将声明和实现分离。

返回值的传递

到目前为止,我们已经学习了执行指令但不返回任何值的函数。然而,在许多情况下,函数需要返回一个值,以便用于其他计算或存储到变量中。这个过程称为返回值的传递。

在本节中,我们将学习返回值函数的工作原理,它们与void类型函数的区别,以及如何在C++中利用这一机制。

实践示例:计算矩形的面积

为了演示返回值的传递,我们将实现一个函数,该函数接收矩形的底边和高,并计算其面积。

#include <iostream>
using namespace std;
// han shu ji suan ju xing de mian ji, bing fan hui jie guo
double calcularArea(double base, double altura) {
    return base * altura;
}
int main() {
    double base, altura;
    
    // qing qiu yong hu shu ru shu ju
    cout << "qing shu ru ju xing de di bian: ";
    cin >> base;
    cout << "qing shu ru ju xing de gao: ";
    cin >> altura;
    // diao yong han shu, jiang jie guo cun chu dao bian liang zhong
    double area = calcularArea(base, altura);
    // xian shi jie guo
    cout << "ju xing de mian ji shi: " << area << endl;
    
    return 0;
}
  1. 函数 calcularArea() 返回一个值
    • 函数接收两个参数:base(底边)和altura(高)。
    • 通过乘法计算面积:base * altura
    • 使用 return 语句将计算结果返回给调用它的程序部分。
  2. main() 中使用返回值
    • 用户输入basealtura的值。
    • 调用函数calcularArea(),并将其返回值存储在变量area中。
    • 最终,在控制台上显示计算结果。
  3. void函数的关键区别

    如果calcularArea()void类型,我们需要在函数内部直接输出结果,而不是返回计算结果以供main()使用。

示例:判断一个数是偶数还是奇数

#include <iostream>
using namespace std;
bool esPar(int numero) {
    return numero % 2 == 0;
}
int main() {
    int numero;
    cout << "qing shu ru yi ge shu: "; cin >> numero;
    if (esPar(numero)) {
        cout << "zhe ge shu shi ou shu." << endl;
    } else {
        cout << "zhe ge shu shi ji shu." << endl;
    }
    return 0;
}

在这个示例中,函数 esPar() 返回 true(如果输入的数是偶数)或 false(如果输入的数是奇数)。main() 使用该返回值来决定输出不同的消息。

递归:调用自身的函数

递归 是一种技术,其中函数调用自身以解决问题,将问题拆分为更小的版本。这在计算阶乘、斐波那契数列以及遍历树等数据结构的算法中特别有用。

示例:计算一个数的阶乘

一个数的阶乘 n 计算如下:n!=n\cdot(n-1)\cdot(n-2) \cdots 3 \cdot2 \cdot 1。这种定义本身就具有递归特性,我们可以用数学公式表示如下:

\begin{array}{rl} 0! &=1\\ n! &= n\cdot(n-1)!\\ \end{array}

基于这个数学定义,我们可以用C++编写如下递归函数:

#include <iostream>
using namespace std;
 
int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    }
    return n*factorial(n - 1);
}
 
int main() {
    int numero;
    cout << "qing shu ru yi ge shu: "; cin >> numero;
    cout << "jie cheng de " << numero << " shi " << factorial(numero) << endl;
    return 0;
}

在此代码中:

  • 函数 factorial(n) 调用自身,传入 n-1,直到达到基准情况(n == 0n == 1)。
  • 该函数通过递归方式计算,将所有值相乘,最终获得结果。

示例:斐波那契数列

斐波那契数列是一个递增序列:1, 1, 2, 3, 5, 8, 13, \cdots。该序列的特点是每个数等于前两个数之和。

如果用 fibo(n) 表示斐波那契函数,则可以用以下递归数学定义:

\begin{array}{rl} fibo(0) &= 1\\ fibo(1) &= 1 \\ fibo(n) &= fibo(n-1) + fibo(n-2) \end{array}

下面是一个C++程序,用递归方式计算斐波那契数列:

#include<iostream>
 
using namespace std;
 
int fibo(int numero){
    if (numero==0||numero==1){
        return 1;
    }
    return fibo(numero-1)+fibo(numero-2);
}
 
int main(){
    int x=0, i=0;
    cout << "qing shu ru yi ge shu: "; cin >> x;
     
    while (i < x){
        cout <<"di " << i+1 << " ge wei zhi de fei bo na qi shu shi: " << fibo(i)<<endl;
        i=i+1;
    }   
}

函数的多重返回值

在C++中,函数可以使用std::pairstd::tuple 或变量引用来返回多个值。

示例:使用 std::pair 返回两个值

#include <iostream>
#include <utility> // shi yong std::pair
using namespace std;
 
pair<int, int> dividir(int a, int b) {
    return make_pair(a / b, a % b);
}
 
int main() {
    int numerador=0, denominador=1;
     
    cout << "qing shu ru chu zi: "; cin >> numerador;
    cout << "qing shu ru mu zi: "; cin >> denominador;
     
    pair<int, int> resultado = dividir(numerador, denominador);
 
    cout << "shang: " << resultado.first << endl;
    cout << "yu shu: " << resultado.second << endl;
 
    return 0;
}

在这里,函数dividir() 返回两个值:整数除法的商和余数。

示例:使用 std::tuple 返回多个值

#include <iostream>
#include <tuple>
using namespace std;
tuple<int, int, int> operaciones(int a, int b) {
    return make_tuple(a + b, a - b, a * b);
}
int main() {
    int suma=0, resta=0, producto=0;
    int a=0, b=0;
    
    cout << "qing shu ru yi ge shu: "; cin >> a;
    
    cout << "qing zai shu ru yi ge shu: "; cin >> b;
    
    std::tie(suma, resta, producto) = operaciones(a, b);
    cout << "jia fa: " << suma << ", jian fa: " << resta << ", cheng fa: " << producto << endl;
    return 0;
}

函数重载(overloading)

函数重载允许定义多个同名函数,但参数的类型或数量不同。这提高了代码的可读性和可重用性。

#include <iostream>
#include <string> // shi yong std::string
#include <cmath>
using namespace std;
// zheng fang xing huo yuan quan de mian ji (yi ge shu ju)
double area(double lado) {
    return lado * lado;
}
// ju xing de mian ji (liang ge shu ju)
double area(double base, double altura) {
    return base * altura;
}
// san jiao xing de mian ji (san ge shu ju)
double area(double a, double b, double c){
    return 0.25*sqrt((a+b+c)*(a+b-c)*(a-b+c)*(-a+b+c));
}
int main() {
    string figura;
    double resultado = 0;
    double l1=0, l2=0, l3=0;
    // wen ti xing zhuang
    cout << "qing shu ru xing zhuang? (zhengfangxing, yuanquan, juxing huo sanjiaoxing): ";
    cin >> figura;
    // pan duan xing zhuang
    if (figura == "zhengfangxing") {
        cout << "bian chang shi duo shao? "; cin >> l1;
        resultado = area(l1);
        cout << "zheng fang xing de mian ji shi: " << resultado << endl;
    } 
    else if (figura == "juxing"){
        cout << "ji shu ru di bian: "; cin >> l1;
        cout << "ji shu ru gao: "; cin >> l2;
        resultado = area(l1, l2);
        cout << "ju xing de mian ji shi: " << resultado << endl;
    } 
    else if (figura == "yuanquan") {
        l1 = 3.141592653;
        cout << "shu ru ban jing: "; cin >> l2;
        resultado = area(l1, l2);
        cout << "yuan quan de mian ji shi: " << resultado << endl;
    } 
    else if (figura == "sanjiaoxing"){
        cout << "shu ru san ge bian chang:" << endl;
        cout << "bian 1: "; cin >> l1;
        cout << "bian 2: "; cin >> l2;
        cout << "bian 3: "; cin >> l3;
        if ((l1+l2+l3)*(l1+l2-l3)*(l1-l2+l3)*(-l1+l2+l3)<0){
            cout << "zhe ge san jiao xing wu fa shi xian";
        }    
        else {
            resultado = area(l1,l2,l3);
            cout << "san jiao xing de mian ji shi: " << resultado << endl;
        }             
    }
    else {
        cout << "bu he fa de xing zhuang." << endl;
    }
    return 0;
}

C++中的内联函数(inline functions)

C++中的inline函数提供了一种优化程序性能的机制,减少了函数调用的开销。与传统的函数调用不同,编译器会尝试在调用的地方直接展开函数代码,而不是跳转到函数的存储位置执行。

内联函数的语法

inline fan_hui_lei_xing han_shu_ming(can_shu_lie_biao) {
    // han_shu_ti
    return zhi; // ru_guo_xu_yao
}

使用inline后,程序不需要跳转到另一个内存地址执行函数,从而减少执行时间。

内联函数 vs 传统函数

特性传统函数内联函数
函数调用程序执行时需要跳转到函数地址。编译器直接将函数代码插入调用处。
运行时间由于调用开销,可能稍慢。对于小型函数,可以加快执行速度。
内存使用函数代码仅存储一次。如果使用过多,会导致可执行文件变大。

内联函数示例

#include <iostream>
using namespace std;
inline int cuadrado(int x) {
    return x * x;
}
int main() {
    cout << "shu zi 5 de ping fang shi: " << cuadrado(5) << endl;
    return 0;
}

🔍 编译器执行过程:

  1. 编译器用 5 * 5 直接替换 cuadrado(5)
  2. 不需要函数调用跳转。
  3. 计算在调用位置直接完成,提高性能。

inline 的优缺点

✅ 优势

  • 消除函数调用开销: 在小型函数和频繁调用的函数中减少执行时间。
  • 编译器可进行优化: 避免使用CPU寄存器和栈,提高效率。
  • 保证函数代码在编译期可用。

❌ 劣势

  • 可能增加二进制文件大小: 如果 inline 函数在程序中被多次调用,代码会在多个地方展开,导致可执行文件增大。
  • 编译器可能会忽略 inline 请求: 当编译器认为函数太大或不适合内联展开时,可能会忽略 inline 修饰符。

关于C++函数的最终思考

C++中的函数是编写模块化、可复用且易维护代码的重要工具。在本课程中,我们从函数的声明、调用和定义等基础知识出发,逐步学习了更高级的技术,如返回值传递、递归、函数重载以及inline函数的优化策略。同时,我们比较了不同的代码组织策略,并探讨了如何根据具体情况选择最佳方案。

正确理解并应用函数不仅能让你的代码更加清晰和高效,还能帮助你解决更复杂的问题,使程序结构更加合理。现在,你已经掌握了在C++中编写专业、可扩展程序的基础知识。巩固这些知识的最佳方式是实践,所以请尝试编写更多不同类型的函数,并在自己的项目中加以应用。继续探索C++的世界,让你的编程能力更上一层楼!

Views: 1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注