20260227 120331 Cpp 入门第七课

20260227_120331_CPP_入门第七课.md

第七章:让程序更聪明——函数

你好!欢迎来到第七章!在前面的章节,我们写的程序都是顺序执行的,代码都挤在 main 函数里。如果有一段代码需要重复使用,难道要复制粘贴很多遍吗?那太麻烦了!这时候就需要函数——把一段代码打包成一个“积木块”,需要的时候随时调用。这一章我们就来学习如何自己创造函数,让程序更聪明、更简洁!


7.1 函数程序范例

先来看一个简单的例子:我们写一个函数,用来计算两个整数的和,然后在 main 里调用它。

#include <iostream>
using namespace std;

// 定义一个函数,名字叫 add,它的功能是返回两个整数的和
int add(int x, int y) {
    int result = x + y;
    return result;      // 把结果返回给调用者
}

int main() {
    int a = 10, b = 20;
    int sum = add(a, b);   // 调用 add 函数,把 a 和 b 传进去,得到返回值
    cout << "a + b = " << sum << endl;

    int c = 30, d = 40;
    cout << "c + d = " << add(c, d) << endl;   // 也可以直接输出返回值

    return 0;
}

运行结果

a + b = 30
c + d = 70

这个程序里,我们定义了一个 add 函数,它接受两个整数参数,返回它们的和。在 main 中,我们可以多次调用它,不用重复写加法代码。


7.2 函数的用法

7.2.1 函数的概念

函数就是一段可以重复使用的代码块,它有自己的名字,你可以通过这个名字来执行它。就像你有一个“做作业”的流程:拿出作业本、写作业、检查、合上本子。你可以把这个流程打包成一个函数叫 doHomework(),每次想写作业时就调用它。

函数的好处:
- 避免重复代码:相同的逻辑只写一次。
- 模块化:把大问题拆成小问题,每个函数解决一个小问题。
- 便于修改和维护:如果需要修改某个功能,只需改函数内部,不用到处找。

7.2.2 语句块与作用域

语句块就是用大括号 {} 括起来的一组语句。比如 if 语句的后面、循环的后面,还有函数体都是语句块。

作用域指的是变量在程序中的有效范围。在一个语句块内定义的变量,只能在这个块内部使用,块外面是访问不到的。这叫做局部变量

#include <iostream>
using namespace std;

int main() {
    int a = 10;   // 这是 main 函数内的局部变量

    if (a > 5) {
        int b = 20;   // b 只在这个 if 块内有效
        cout << a << " " << b << endl;   // 可以访问 a 和 b
    }

    // cout << b;   // 错误!b 在这里已经不存在了
    return 0;
}

函数也是语句块,函数内部定义的变量只属于这个函数,其他函数不能直接访问。

7.2.3 自定义函数介绍

定义一个函数的基本格式:

返回值类型 函数名(参数列表) {
    // 函数体:要执行的代码
    return 返回值;   // 如果返回值类型不是 void,必须返回对应类型的值
}
  • 返回值类型:函数执行完后要返回什么类型的数据,比如 intdoublechar,如果没有返回值就用 void
  • 函数名:自己起名字,要符合变量命名规则,最好能说明功能。
  • 参数列表:函数需要的输入数据,可以有多个,用逗号分隔,每个参数要写明类型和名字。也可以没有参数,写成 ()(void)
  • 函数体:具体执行的代码。
  • return 语句:把结果返回给调用者。如果返回值类型是 void,可以没有 return,或者只写 return; 表示结束函数。

例子:一个没有参数、没有返回值的函数

#include <iostream>
using namespace std;

// 输出欢迎信息
void sayHello() {
    cout << "你好,欢迎学习C++!" << endl;
}

int main() {
    sayHello();   // 调用函数
    sayHello();   // 可以多次调用
    return 0;
}

例子:有参数但没有返回值

#include <iostream>
using namespace std;

// 输出两个数的和,但不返回结果
void printSum(int x, int y) {
    cout << x << " + " << y << " = " << x + y << endl;
}

int main() {
    printSum(3, 5);
    printSum(10, 20);
    return 0;
}

7.2.4 函数的返回值

return 语句有两个作用:
1. 结束函数的执行,返回到调用它的地方。
2. 把后面的值返回给调用者。

例子:返回较大值的函数

#include <iostream>
using namespace std;

int max(int x, int y) {
    if (x > y) {
        return x;
    } else {
        return y;
    }
}

int main() {
    int a = 15, b = 20;
    int m = max(a, b);
    cout << "较大的数是:" << m << endl;
    return 0;
}

注意:如果函数声明了返回值类型(非 void),那么所有分支都必须有 return,否则编译错误。

7.2.5 函数的形参与实参

  • 形参(形式参数):定义函数时写的参数,就像占位符,告诉调用者需要传什么类型的数据。比如 int add(int x, int y) 中的 xy
  • 实参(实际参数):调用函数时实际传递的值,比如 add(3, 5) 中的 35

调用时,实参会复制给形参,然后在函数内部使用形参。函数内部对形参的修改不会影响实参(除非传的是指针或引用,但初学者先不管)。

#include <iostream>
using namespace std;

void change(int x) {
    x = 100;   // 修改形参
    cout << "函数内部 x = " << x << endl;
}

int main() {
    int a = 10;
    change(a);
    cout << "main 中 a = " << a << endl;   // a 还是 10,没变
    return 0;
}

7.2.6 函数的声明

在C++中,函数必须先声明或定义,然后才能调用。如果函数的定义写在调用之后,就需要提前声明(也叫函数原型)。

#include <iostream>
using namespace std;

// 函数声明,告诉编译器有这个函数,后面再定义
int max(int x, int y);

int main() {
    int a = 5, b = 8;
    cout << max(a, b) << endl;
    return 0;
}

// 函数定义
int max(int x, int y) {
    return (x > y) ? x : y;
}

函数声明只需要写返回值类型、函数名和参数类型,可以省略参数名(但建议保留,便于阅读)。

7.2.7 函数的调用与递归

调用很简单,写函数名加括号和实参即可。

递归:函数自己调用自己。就像俄罗斯套娃,一层套一层。递归必须有一个结束条件,否则会无限循环。

例子:用递归计算阶乘(n! = 1×2×…×n)

#include <iostream>
using namespace std;

int factorial(int n) {
    if (n == 0 || n == 1) {   // 递归结束条件
        return 1;
    } else {
        return n * factorial(n - 1);   // 自己调用自己
    }
}

int main() {
    int n;
    cout << "请输入一个整数:";
    cin >> n;
    cout << n << "! = " << factorial(n) << endl;
    return 0;
}

执行过程(比如 n=3):
- factorial(3) 返回 3 * factorial(2)
- factorial(2) 返回 2 * factorial(1)
- factorial(1) 返回 1
- 然后一步步返回:factorial(2) = 21=2, factorial(3)=32=6。

递归虽然有趣,但初学者容易绕晕。刚开始只要理解概念就好。

7.2.8 数字查找之顺序和二分

这一节结合函数来实现查找算法。

顺序查找

在一个数组中找一个数,从第一个开始一个一个比较,直到找到或找完。

#include <iostream>
using namespace std;

// 顺序查找函数:在数组a中找key,返回下标,找不到返回-1
int sequentialSearch(int a[], int n, int key) {
    for (int i = 0; i < n; i++) {
        if (a[i] == key) {
            return i;   // 找到了,返回下标
        }
    }
    return -1;   // 没找到
}

int main() {
    int arr[] = {34, 67, 12, 89, 45, 23, 56};
    int n = sizeof(arr) / sizeof(arr[0]);
    int key;
    cout << "请输入要查找的数:";
    cin >> key;
    int pos = sequentialSearch(arr, n, key);
    if (pos != -1) {
        cout << "找到了,位置是:" << pos << endl;
    } else {
        cout << "没找到" << endl;
    }
    return 0;
}
二分查找

二分查找要求数组必须是有序的(升序)。它每次都和中间元素比较,如果等于就找到;如果小于中间,就在左半部分找;如果大于,就在右半部分找。这样每次都能排除一半。

#include <iostream>
using namespace std;

// 二分查找函数:在升序数组a中找key,返回下标,找不到返回-1
int binarySearch(int a[], int n, int key) {
    int left = 0, right = n - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;   // 防止溢出
        if (a[mid] == key) {
            return mid;
        } else if (a[mid] < key) {
            left = mid + 1;   // 去右半部分
        } else {
            right = mid - 1;  // 去左半部分
        }
    }
    return -1;
}

int main() {
    int arr[] = {12, 23, 34, 45, 56, 67, 89};   // 必须有序
    int n = sizeof(arr) / sizeof(arr[0]);
    int key;
    cout << "请输入要查找的数:";
    cin >> key;
    int pos = binarySearch(arr, n, key);
    if (pos != -1) {
        cout << "找到了,位置是:" << pos << endl;
    } else {
        cout << "没找到" << endl;
    }
    return 0;
}

对比:顺序查找适用于任何数组,但慢;二分查找快,但要求数组有序。


7.3 编程实例讲解

实例1:判断素数函数

写一个函数,判断一个整数是否是素数(只能被1和自身整除的数)。

#include <iostream>
#include <cmath>   // 用sqrt函数
using namespace std;

bool isPrime(int n) {
    if (n <= 1) return false;
    for (int i = 2; i <= sqrt(n); i++) {
        if (n % i == 0) {
            return false;
        }
    }
    return true;
}

int main() {
    int num;
    cout << "请输入一个整数:";
    cin >> num;
    if (isPrime(num)) {
        cout << num << " 是素数" << endl;
    } else {
        cout << num << " 不是素数" << endl;
    }
    return 0;
}

实例2:求最大公约数函数(辗转相除法)

#include <iostream>
using namespace std;

int gcd(int a, int b) {
    while (b != 0) {
        int temp = a % b;
        a = b;
        b = temp;
    }
    return a;
}

int main() {
    int x, y;
    cout << "请输入两个整数:";
    cin >> x >> y;
    cout << "最大公约数是:" << gcd(x, y) << endl;
    return 0;
}

实例3:递归求斐波那契数列第n项

斐波那契数列:1, 1, 2, 3, 5, 8, 13, … 从第三项起,每一项等于前两项之和。

#include <iostream>
using namespace std;

int fib(int n) {
    if (n == 1 || n == 2) {
        return 1;
    } else {
        return fib(n - 1) + fib(n - 2);
    }
}

int main() {
    int n;
    cout << "请输入项数:";
    cin >> n;
    cout << "第" << n << "项是:" << fib(n) << endl;
    return 0;
}

注意:递归虽然简单,但效率低,因为重复计算很多。可以用循环改进。

实例4:数组排序函数

把冒泡排序封装成函数,可以对任意整数数组排序。

#include <iostream>
using namespace std;

// 冒泡排序函数,升序
void bubbleSort(int a[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (a[j] > a[j + 1]) {
                int temp = a[j];
                a[j] = a[j + 1];
                a[j + 1] = temp;
            }
        }
    }
}

// 输出数组函数
void printArray(int a[], int n) {
    for (int i = 0; i < n; i++) {
        cout << a[i] << " ";
    }
    cout << endl;
}

int main() {
    int arr[] = {34, 67, 12, 89, 45};
    int n = sizeof(arr) / sizeof(arr[0]);

    cout << "排序前:";
    printArray(arr, n);

    bubbleSort(arr, n);

    cout << "排序后:";
    printArray(arr, n);

    return 0;
}

7.4 第7章编程作业

恭喜你学完了函数!现在来挑战几个综合题目,检验一下学习成果。

作业1:计算器函数

写四个函数:addsubmuldiv(除法要考虑除数为0的情况)。然后在 main 中让用户输入两个数和运算符,调用对应的函数计算结果。

作业2:数组统计函数

写一个函数,接受一个整数数组和数组长度,返回数组中的最大值、最小值、平均值(可以返回多个值吗?初学者可以用引用参数或返回结构体,但这里可以定义多个函数分别求,或者用全局变量。建议先定义多个函数:int maxArray(int a[], int n)int minArray(...)double avgArray(...))。

作业3:进制转换函数

写一个函数,将一个十进制整数转换为二进制字符串(用string返回)。提示:不断除以2取余,最后反转字符串。

作业4:递归求幂

用递归实现求 x 的 n 次幂(n为非负整数)。x^n = x * x^(n-1),当n=0时返回1。

作业5:猜数字游戏(函数版)

把猜数字游戏的主要逻辑写成函数:比如 void playGame(int maxNumber, int maxTries),在 main 中调用开始游戏。


好了,你已经学会了如何自己创造函数,把程序拆分成一个个小模块,这样代码更清晰、更容易维护。加油!🚀