← 课程目录
第 11 讲 模块四:用函数组织代码

函数与参数

11 / 25
💡
本节课目标
从这一讲开始进入模块四:用函数组织代码。今天分四步走: 先明白为什么要用函数,再学最简单的无参函数怎么定义和调用, 接着学怎么用参数让函数处理不同的数据,最后弄清形参实参的区别, 以及参数传递时到底发生了什么。
开场

重复的代码,谁来维护?

📖 理论讲解

一段容易抄错的代码

假设程序要在开头和中间各打印一次欢迎横幅,最直接的写法是抄两遍

bad_copy_paste.cpp CPP
#include <iostream>

int main() {
    std::cout << "========================" << std::endl;
    std::cout << "     欢迎使用计算器" << std::endl;
    std::cout << "========================" << std::endl;

    std::cout << "请输入数字:" << std::endl;
    // ……中间一堆处理代码……

    std::cout << "========================" << std::endl;
    std::cout << "     欢迎使用计算器" << std::endl;
    std::cout << "========================" << std::endl;
    // 同样三行,又抄了一遍
    return 0;
}
⚠️
抄两遍的代价
横幅内容改一个字,就要记得两处都改。如果横幅要出现在十个地方呢? 漏改一处,界面就会不一致——这就是重复代码埋下的坑。
📖 理论讲解

函数:把代码打包、取个名字

其实你早就在用函数了——rand()srand()time() 都是别人写好、封装成函数的代码,你只管调用,不用管它内部怎么实现。

函数就是把一段代码打包、取一个名字。以后需要这段逻辑时, 直接喊它的名字(调用)就行,不用再抄一遍。

第一段

最简单的函数:无参数、不返回值

📖 理论讲解

语法拆解

部分例子含义
返回类型void这个函数不返回值(下节课细讲)
函数名printBanner调用时用这个名字
参数列表()圆括号,空的表示不需要参数
函数体{ … }大括号里是要执行的代码
📖 理论讲解

用函数解决重复问题

把打印横幅的三行代码包进 printBanner(),需要的地方直接调用它:

print_banner.cpp CPP
#include <iostream>

void printBanner() {
    std::cout << "========================" << std::endl;
    std::cout << "     欢迎使用计算器" << std::endl;
    std::cout << "========================" << std::endl;
}

int main() {
    printBanner();
    std::cout << "请输入数字:" << std::endl;
    // ……中间一堆处理代码……
    printBanner();
    return 0;
}
Python

Python:def + 冒号 + 缩进

def print_banner():

print("=====")

print("欢迎")

VS
C++

C++:返回类型 + 函数名 + 大括号

void printBanner() {

std::cout << "=====";

}

调用别忘了圆括号
printBanner(); 才是调用;只写 printBanner(不带括号)不会执行函数—— 这和调用 rand()time() 是同一套规矩。
⚠️
调用前,编译器要先认识函数
编译器处理到 printBanner(); 时,需要已经见过这个函数的定义或声明。 现阶段最简单的做法,是把自定义函数完整地写在 main 前面;以后也可以先写 void printBanner(); 这样的函数声明,再把函数定义放到 main 后面。
⚡ 动手实操 约 5 分钟

写你的第一个函数

仿照 print_banner.cpp,把下面的骨架补完整:在 sayHi() 函数体里加一行 std::cout,打印一句你想说的话。

say_hi.cpp CPP
#include <iostream>

void sayHi() {
    // 在这里写一行 std::cout,打印一句你想说的话
}

int main() {
    sayHi();
    sayHi();
    return 0;
}

补完后运行,确认那句话被打印了两次——这就是“只写一次,用很多次”。

第二段

一个参数:让函数按需办事

📖 理论讲解

固定内容 vs 可定制内容

sayHi() 每次打印的内容都写死了。如果想跟不同的人打招呼, 总不能给“小明”“小红”各写一个函数吧?

参数能解决这个问题:定义函数时,在括号里声明一个“占位”的变量, 调用时把具体的值传进去。

greet.cpp CPP
#include <iostream>
#include <string>

void greet(std::string name) {
    std::cout << "你好," << name << "!欢迎来到 C++ 的世界。" << std::endl;
}

int main() {
    greet("小明");
    greet("小红");
    return 0;
}
📖 理论讲解

读一下这段代码

std::string name 是函数括号里声明的参数——它就像一个 还没装东西的容器。调用 greet("小明") 时,"小明" 被装进 name,函数体内的 name 这时就等于 "小明"。 再调用 greet("小红")name 又变成了 "小红"

⚡ 动手实操 约 5 分钟

换一个人打招呼

greet.cpp 里的两次调用,换成向你自己的名字同桌的名字打招呼,运行看看效果。

第三段

多个参数:按顺序对号入座

📖 理论讲解

参数可以有好几个

参数不止一个时,括号里用逗号隔开;调用时传的值,也要按同样的顺序 一一对应上:

print_info.cpp CPP
#include <iostream>
#include <string>

void printInfo(std::string name, int age) {
    std::cout << name << " 今年 " << age << " 岁。" << std::endl;
}

int main() {
    printInfo("小明", 12);
    printInfo("小红", 13);
    // printInfo(12, "小明");   // 顺序反了,类型对不上,编译会报错
    return 0;
}
📖 理论讲解

读一下这段代码

调用 printInfo("小明", 12) 时,第一个实参 "小明" 对应第一个形参 name第二个实参 12 对应第二个形参 age——完全按位置一一对应,和形参、实参叫什么名字没关系。

⚠️
顺序和类型都不能错
printInfo(std::string name, int age) 规定了第一个参数是 std::string、第二个是 int。调用时如果写成 printInfo(12, "小明"),类型对不上,编译器直接报错,根本运行不起来。
第四段

形参与实参:占位符和真数据

📖 理论讲解

两个名字,两个时刻

概念出现在哪对应 print_info.cpp 里的例子
形参定义函数时,括号里的变量nameage
实参调用函数时,传进去的具体值"小明"12

目前这些参数采用的是按值传递。调用 printInfo("小明", 12) 那一刻,实参的值被复制给对应的形参,函数体内接下来用的都是这份副本。

📖 理论讲解

“复制”这件事,其实你早就见过

普通的赋值 = 就是把值复制一份给另一个变量,两者从此互不影响:

copy_assign.cpp CPP
int a = 5;
int b = a;   // b 复制了一份 a 的值
b = 10;      // 只改了 b,a 不受影响

std::cout << a << " " << b << std::endl;   // 输出:5 10

对于本节课使用的按值传递,发生的事情和这个很相似——只不过“复制”发生在 调用函数的那一刻。以后还会接触不复制整个值的其他传递方式。

try_double.cpp CPP
#include <iostream>

void tryDouble(int x) {
    x = x * 2;
    std::cout << "函数里 x = " << x << std::endl;
}

int main() {
    int num = 5;
    tryDouble(num);
    std::cout << "函数外 num = " << num << std::endl;
    return 0;
}
⚠️
改动形参,不影响外面的实参
在当前这种按值传递中,函数拿到的是一份拷贝。在函数内部改动 x,只是改了这份拷贝, main 里的 num 原封不动。
⚡ 动手实操 约 8 分钟

验证“拷贝”这件事

try_double.cpp 跑起来,运行前先猜两行输出是否一样,再运行验证。

  1. 函数里的 x 和函数外的 num,最终打印出来是同一个值吗?
  2. 试着把 x = x * 2; 挪到 main 里对 num 直接执行一次,对比两种写法的效果。
实战

动手写四个函数

练习 11-1

分割线函数

写一个无参数、无返回值的函数 printDivider(),打印一行由 - 组成的分割线。在 main 里调用三次,把不同内容隔开。

练习 11-2

奇偶判断函数

写一个函数 printParity(int n),直接在函数里用 cout 打印 n 是“偶数”还是“奇数”。在 main 里对好几个不同的数分别调用它。

提示:判断逻辑就是模块二的 if / else,只是这次包进了函数里。

练习 11-3

较大数函数

写一个函数 printMax(int a, int b),比较两个整数,打印较大的那个。 在 main 里用几组不同的数字测试。

提示:函数体内部用模块二的 if / else 比较 ab;这是一个两个参数的函数,注意参数声明和调用时顺序要对应。

练习 11-4

打印矩形

写一个函数 printRectangle(int width, int height),打印一个由 * 组成的、width 列、height 行的实心矩形。

提示:回忆模块三的循环——外层循环控制行数,内层循环控制每行打印几个 *, 两层循环嵌套着写。

收尾

小结

📖 理论讲解

这节课你掌握了

能力用到的工具
把代码打包、取名字void 函数名() { }
让函数按不同数据办事参数(parameter)
让函数同时按多个数据办事多个参数,用逗号隔开
区分“定义时”与“调用时”形参 / 实参
知道改参数不会影响外部变量传值(拷贝)
💡
下节课预告
现在的函数只能“做事”——比如自己 cout 打印结果。 如果想让函数算出一个值,再拿回 main 里接着用, 就需要 return。下节课学返回值。