函数
同一段代码要在好几个地方用,不用抄好几遍:给它起个名字,写成一个函数,要用的时候调用它。main 本身就是一个函数,现在来写你自己的。
1#include <iostream>2 3double with_tax(double price) {4 return price * 1.08;5}6 7int main() {8 std::cout << with_tax(100) << '\n';9 std::cout << with_tax(250) << '\n';10 return 0;11}
108 270
with_tax 拿到一个价格,算出加税以后的价格交回来。main 调用了它两次,每次给的价格不一样。
定义和调用
一个函数的定义有四部分:
| 部分 | 上面的例子里 |
|---|---|
| 返回类型:交回来的值是什么类型 | double |
| 函数名 | with_tax |
| 参数:括号里,调用时要给的值 | double price |
| 函数体:花括号里,调用时要跑的代码 | return price * 1.08; |
什么都不用交回来的函数,返回类型写 void。什么都不用给的函数,括号里空着:
1#include <iostream>2 3void line() {4 std::cout << "----------\n";5}6 7int main() {8 line();9 std::cout << "购物清单\n";10 line();11 return 0;12}
---------- 购物清单 ----------
调用时,程序跳进函数,从头跑到尾,再回到调用它的地方接着往下跑。点单步看它怎么跳进跳出。调用时括号不能省:只写 line; 什么也不会发生。
参数
参数让同一个函数每次做的事稍有不同。几个参数用逗号隔开,每个都要写类型;调用时按同样的顺序给值:
1#include <iostream>2#include <string>3 4void show_item(std::string name, int count, double price) {5 std::cout << count << " 份" << name << ":" << count * price << " 元\n";6}7 8int main() {9 show_item("牛奶", 2, 6.5);10 show_item("面包", 1, 12);11 return 0;12}
2 份牛奶:13 元 1 份面包:12 元
调用时给的值("牛奶"、2、6.5)叫实参,它们按顺序放进参数 name、count、price 里。个数不对、类型对不上,编译器会报错。
返回值
return 做两件事:把一个值交回给调用的地方,并且马上结束这个函数。交回来的值可以存进变量、拿去算,也可以直接显示:
1#include <iostream>2 3int bigger(int a, int b) {4 if (a > b) {5 return a;6 }7 return b;8}9 10int main() {11 int best = bigger(78, 91);12 std::cout << best << '\n';13 std::cout << bigger(5, 3) + bigger(2, 4) << '\n';14 return 0;15}
91 9
a > b 成立时,return a; 就结束了函数,后面的 return b; 不会跑。
这段程序显示什么?
1#include <iostream>2 3int twice(int n) {4 return n * 2;5 std::cout << "算好了\n";6}7 8int main() {9 std::cout << twice(twice(3)) << '\n';10 return 0;11}
里面的 twice(3) 先算,得到 6;外面的 twice(6) 再得到 12。return 一跑,函数就结束了,所以「算好了」一次也不显示。
先声明,后定义
编译器从上往下读代码。在 main 里调用一个写在下面的函数,读到调用的时候它还不认识这个名字:
1#include <iostream>2 3int main() {4 std::cout << square(4) << '\n';5 return 0;6}7 8int square(int n) {9 return n * n;10}
main.cpp: In function 'int main()':
main.cpp:4:18: error: 'square' was not declared in this scope
4 | std::cout << square(4) << '\n';
| ^~~~~~'square' was not declared in this scope:在这里还没声明过 square。要么把函数挪到 main 上面,要么在上面先写一行声明(也叫原型):只有返回类型、名字和参数,以分号结尾,没有函数体:
1#include <iostream>2 3int square(int n);4 5int main() {6 std::cout << square(4) << '\n';7 return 0;8}9 10int square(int n) {11 return n * n;12}
16
声明告诉编译器「有这么个函数,长这样」,函数体可以放到后面再写。函数多的时候,先把声明列在上面,main 紧跟着,读的人一眼就能看到程序是做什么的。
传值:改不到外面
调用函数时,实参的值被复制一份,放进参数里。函数里改参数,改的是这份复制,外面的变量不受影响:
1#include <iostream>2 3void add_bonus(int score) {4 score += 10;5 std::cout << "函数里:" << score << '\n';6}7 8int main() {9 int score = 80;10 add_bonus(score);11 std::cout << "main 里:" << score << '\n';12 return 0;13}
函数里:90 main 里:80
两个变量都叫 score,但它们是两个不同的变量。点单步看看:调用 add_bonus 时,画面上多出一层,那是这次调用自己的地方,里面有它自己的 score。函数里加了 10,变的只是这一层的 score;函数结束,这一层就没了,main 里的 score 一直是 80。
想让函数的结果留下来,就把它 return 回去,再由调用的地方存起来:
1#include <iostream>2 3int add_bonus(int score) {4 return score + 10;5}6 7int main() {8 int score = 80;9 score = add_bonus(score);10 std::cout << score << '\n';11 return 0;12}
90
(第 10 课还有另一个办法:引用。)
返回 std::string
函数可以返回任何类型,包括 std::string。把一段判断装进函数,用的地方就很清楚:
1#include <iostream>2#include <string>3 4std::string grade_of(int score) {5 if (score >= 90) {6 return "优秀";7 } else if (score >= 60) {8 return "及格";9 }10 return "不及格";11}12 13int main() {14 std::cout << "小林:" << grade_of(95) << '\n';15 std::cout << "小周:" << grade_of(72) << '\n';16 std::cout << "小陈:" << grade_of(48) << '\n';17 return 0;18}
小林:优秀 小周:及格 小陈:不及格
忘了 return
返回类型不是 void 的函数,每一条路都要以 return 结束。漏了一条,g++ 会在输出上面给出一条警告(warning:)。警告不会让编译失败,程序照样运行:
1#include <iostream>2 3int sign(int n) {4 if (n > 0) {5 return 1;6 } else if (n < 0) {7 return -1;8 }9}10 11int main() {12 int a = sign(5);13 int b = sign(0);14 std::cout << a << " " << b << '\n';15 return 0;16}
main.cpp: In function 'int sign(int)': main.cpp:9:1: warning: control reaches end of non-void function [-Wreturn-type] 9 | } | ^ control reached the end of 'sign' without a return value
发生了什么: 停在第 9 行:函数「sign」说好要交回一个 int,可是走到最后也没有 return。
control reaches end of non-void function:函数跑到了结尾,却没有 return。sign(5) 还好好的;sign(0) 两个条件都不成立,函数什么也没交回来,这是未定义行为,程序在这里就出事了,一行也没显示。看到警告别跳过:补上最后的 return 0; 再运行。
练一练
写好代码,点检查。提示随便看,实在想不出来再看答案。
长方形的面积
✓写一个函数 area,接收长和宽(都是 int),返回面积。main 已经写好了,程序要显示 面积:24。
面积:24
1#include <iostream>2 3int main() {4 std::cout << "面积:" << area(6, 4) << '\n';5 return 0;6}
摄氏转华氏
✓写一个函数 to_fahrenheit,接收摄氏度(double),返回华氏度(摄氏 × 1.8 + 32)。显示 25 度 = 77 华氏度。
25 度 = 77 华氏度
1#include <iostream>2 3int main() {4 double c = 25;5 6 return 0;7}
修好它:没加上的库存
✓restock 本想给库存加 20,可 main 里的 stock 还是 5。不用引用,改好它,让程序显示 库存:25。
库存:25
1#include <iostream>2 3void restock(int stock) {4 stock += 20;5}6 7int main() {8 int stock = 5;9 restock(stock);10 std::cout << "库存:" << stock << '\n';11 return 0;12}
这一课学会了
- 函数 = 返回类型 + 名字 + 参数 + 函数体;不交回值的写
void。调用时写名字和括号,给实参。 return交回一个值,并且马上结束函数。- 编译器从上往下读:函数要在调用之前定义,或者先写声明(原型)
int square(int n);。 - 传值:实参被复制进参数,函数里改参数不影响外面的变量;想留下结果,就
return回去。 - 不是
void的函数,每条路都要return;漏了会有警告control reaches end of non-void function,别跳过警告。