Chapter7 函数——C++的编程模块

7.1 复习函数的基本知识

parameter和argument的异同点:

parameter是指函数定义中参数,而argument指的是函数调用时的实际参数。 简略描述为:parameter=形参(formal parameter), argument=实参(actual parameter)。 在不很严格的情况下,现在二者可以混用,一般用argument,而parameter则比较少用。

c7001

类比:打电话去饭店预约,饭店前台只需要知道你有几个人,几个大人几个小孩,你需要什么类型的包厢;前台并不需要知道你这几个人都叫什么名字.

7.2 函数参数和按值传递

1

2

上面的n, i名字相同,,但是在内存里面存放的地址不一样.因此是不同的.

打比方:一班有人叫张三,二班有人叫张三,但他们两个是完完全全的不相同的两个人.(只有名字相同)

3

程序清单7.3 twoarg.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;
void n_chars(char c, int n);
int main(void)
{
char ch;
int times;
cout << "enter a character: ";
cin >> ch;
while (ch != 'q')
{
cout << "enter an integer:";
cin >> times;
n_chars(ch, times);
cout << "enter a character: ";
cin >> ch;
}
return 0;
}

void n_chars(char c, int n)
{
while (n--)
cout << c;
cout << endl;
}

4

5

个人额外的思考:

我第一次看到这里的时候,认为可以按照他之前的做法改成do while形式.但是经过我这次细细的复习之后,个人感觉这里不可以按照他之前的做法改成do while形式,因为

并不是要用户先输入数据,然后再判断测试,要不要跳出循环

因为他这里还要cin>>times,,如果一开始输入q(quit,退出)的话,就直接退出循环了,而不用cin>>times.

若是改成do while循环,则必须再循环体内cin>>ch,,cin>>times后才能对while(测试语句)里面的 测试语句进行判断,因此不符合题目要求.

[!CAUTION]

怪不得那么多用到死循环的程序,都是用的while循环,先在循环体外cin>>ch,然后进入while(1) 的循环,显示1,2,3,4……等等选项.就算你第一次输入的是q,也能第一次就立马退出循环,不显示后面的选项表格.

经典while结构

cout << “Please Enter your character: “ << endl;

cin>>ch
while(ch != ‘q’){

cout << “选项1” << endl;

cout << “选项2” << endl;

cout << “选项3” << endl;

cout << “选项4” << endl;

cout << “enter q to quit” << endl;

cout << “Please Enter your character: “ << endl;

cin>>ch;

}

如果你用的是do while循环,那么你第一次输入q和第一次后面输入q,结果都是还是显示一遍1,2,3,4……等等选项,就不符合要求.

经典do while循环结构

do{

cout << “Please Enter your character: “ << endl;

cin>>ch

cout << “选项1” << endl;

cout << “选项2” << endl;

cout << “选项3” << endl;

cout << “选项4” << endl;

cout << “enter q to quit” << endl;

}while(ch != ‘q’);
备注

这里用do while就是为了省一次cin>>ch,,,不然就没有用do while的必要了.你看下面这种do while 的写法,就和while循环一样的输出cin>>ch的次数,能防止除了第一次输入q以外的输入q退出但是程序还是输出选项表格的情况,但第一次还是防止不了;;;同时这种do while还是和while一样敲cin>>ch的次数,事实上,根本就没人把do while写成这样子.

既然大家都用经典写法,那我也写经典写法.

cout << “Please Enter your character: “ << endl;

cin>>ch

do{

cout << “选项1” << endl;

cout << “选项2” << endl;

cout << “选项3” << endl;

cout << “选项4” << endl;

cout << “enter q to quit” << endl;

cout << “Please Enter your character: “ << endl;

cin>>ch

}while(ch != ‘q’);

反正记住经典写法就够了,任何事情都轮不到你创新.

另一个就是之前笔记里面的自己的思考

程序清单7.3 twoarg.cpp自己对输出n次字符c的一些感悟

如果你想下面这样写

void n_chars(char c, int n) { while (–n) { cout << c; } }

只会输出n - 1次字符c.

6

我们捋一捋其中的思路,就是对于n.

如果n >0,,,n是1,2,3,,,,等等,那么从数字1开始到n,就会刚刚好有n个数字(就算n = 1 也是成立的).

  1. 先讨论n ≥ 1的情况,数字1,2,,,,一直到数字n这些数字的个数是n个,而且他们都是大于0的,因此用每个数字都用while做一个判断,大于0就输出一次字符c,就能刚好输出n次字符c.
  2. 接着优化省去大于0的判断
  3. 重点是将n减减放在哪里,只要把n减减放在while循环体花括号里面,那么都能保证是数字1,2,,,,一直到数字n这些数字的个数是n个,是他们n个在做大于0的判断.
1
2
3
4
5
6
7
8
void n_chars(char c, int n)
{
while (n)
{
cout << c;
n--;
}
}
1
2
3
4
5
6
7
8
void n_chars(char c, int n)
{
while (n)
{
n--;
cout << c;
}
}
  1. 但是为了省略,大家都会将n减减放到while 的判断表达式里面,这样就有while(n—)和while(—n)的区别.根据先用后减和先减后用的两个原则,可以判断
  2. while(n—)等于
1
2
3
4
5
6
7
8
void n_chars(char c, int n)
{
while (n)
{
n -= 1;
cout << c;
}
}

while(—n)等于

1
2
3
4
5
6
7
8
9
void n_chars(char c, int n)
{
n -= 1;
while (n)
{
cout << c;
n -= 1;
}
}
  1. 结论就是while(n—)循环体里面的语句执行n次(n = 1 , 2, ,,,, n)
  2. 结论就是while(—n)循环体里面的语句执行n - 1次(n = 1, 2, ,,, n)
  3. 上面两个对n = 1也成立.
  4. 这样就算把这个讲清楚了,记住结论,下次可以很快的选择写法.

如果n ≤ 0 从数字1开始到n,就会有0个数字.对于上面两个写法,循环体里面的语句都执行0次.

上面是我第一次看的想法

我这次复习来看的话,都不是好的写法,就写成最好,不要绕给任何人

1
2
3
4
5
while(n)
{
......;//do something
n--;
}

7.2.2 另外一个接受两个参数的函数

发现有的时候自己看书会理解的更快,这个老师也是照着书讲,但是没有体系的讲,照着书还会有信息都筛选让我们这些听课的人们有遗漏.一手的总比二手的信息来的好,除非你做成了课件那样真正嚼烂了且经过很多年很多人实践的好课件资料.

自己尝试敲了一边代码,一开始没用到n和p,都是直接numbers–和picks–

而且也是收到启发才讲double result = 1.0写进去,放到循环里面

反正这本书里面有很多例子,你别看任何提示,只看他给的要求和描述,能不能写出一样的代码,还是很有锻炼意义的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
int main(void)
{
int numbers, picks;
cin >> numbers;
cin >> picks;
long double result = 1.0;
// 为了保护numbers和picks的值不被改变,所以用n和p拷贝一份副本;
for (int n = numbers, p = picks; p > 0; p--)
{
cout << n << " " << p << endl;
result = result * n / p;
n--;
}

cout << result << endl;

return 0;
}

书中程序清单7.4 lotto.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;
long double probability(unsigned numbers, unsigned picks);
int main(void)
{
double total, choices;
cout << "Enter the total number of choices on the game card and\n"
"the number of picks allowed:\n";
while ((cin >> total >> choices) && choices <= total)
{
cout << "You have one chance in ";
cout << probability(total, choices);
cout << " of winning.\n";
cout << "Next two numbers (q to quit): ";
}
cout << "bye\n";
return 0;
}

long double probability(unsigned numbers, unsigned picks)
{
long double result = 1.0;
long double n;
unsigned p;
for (n = numbers, p = picks; p > 0; p--, n--)
{
result *= (n / p); // 不能让n / p得到一个取整,因此要把n设置成long double类型
}
return result;
}

对于while(cin>>n)类型的博客

深入理解while(cin>>n,n)与cin>>n;while(n)

https://www.acwing.com/blog/content/40428/

原书的前面应该讲了while(cin >> n)这个结构,不急,后面会把这个内容补上来的.

7.3 函数和数组

为什么凡是涉及到数组的传参不仅会传数组名,往往还会传一个int len ,也就是数组的长度,因为你在子函数内

cout << “size of arr: “ << sizeof(arr) << endl;

的结果是8,也就是arr是int *类型的,指针变量的sizeof之后才是8Byte.完美接受了数组名的含义(该数组第一个元素的地址).

也就是说arr是int * 类型表明,子函数只知道该数组的第一个元素的地址和每一个元素的类型(这里是int类型),,但是不知道数组结束在哪里,因此还要传入一个参数int len来确定数组在内存当中的所有地址范围.

这是这一届最核心的知识点,我把代码和结果贴上来.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;
const int ArSize = 8;
int sum_arr(int *arr, int len);
int main(void)
{
int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128};
cout << "cookies address: " << cookies << endl;
cout << "size of cookies: " << sizeof(cookies) << endl;
int sum = sum_arr(cookies, ArSize);
cout << "Total cookies eaten: " << sum << endl;
return 0;
}

int sum_arr(int *arr, int len)
{
int total = 0;
for (int i = 0; i < len; i++)
{
total += arr[i];
}
cout << "size of arr: " << sizeof(arr) << endl;
cout << "arr address is : " << arr << endl;
return total;
}

image-20241030212332349

更全的书上的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
using namespace std;
const int ArSize = 8;
int sum_arr(int *arr, int len);
int main(void)
{
int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128};
cout << "cookies address: " << cookies << endl;
cout << "size of cookies: " << sizeof(cookies) << endl;
int sum = sum_arr(cookies, ArSize);
cout << "Total cookies eaten: " << sum << endl;

sum = sum_arr(cookies, 3);
cout << "First three eaten : " << sum << endl;
sum = sum_arr(4 + cookies, 4);
cout << "last three eaten : " << sum << endl;
return 0;
}

int sum_arr(int *arr, int len)
{
int total = 0;
for (int i = 0; i < len; i++)
{
total += arr[i];
}
cout << "size of arr: " << sizeof(arr) << endl;
cout << "arr address is : " << arr << endl;
return total;
}

7.3.3 更多数组函数示例

7.4 函数和二维数组

7.5 函数和C-风格字符串

7.6 函数和结构

7.7 函数和String对象

7.8 函数和array对象

7.9 递归

7.10 函数指针

7.11 总结

7.12 复习题

7.13 编程练习

💡 有关C++PrimerPlus上的问题,欢迎您在底部评论区留言,一起交流~