From 99fce4c8bf4691024f0269a4e3cdfbceca1a87fb Mon Sep 17 00:00:00 2001 From: Shujia Huang Date: Thu, 5 Aug 2021 09:04:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4chapter07?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- practice/booknotes.md | 341 ++++++++++++++++++++++++++++++++- source/chapter07/arfupt.cpp | 72 +++++++ source/chapter07/arfupt1.cpp | 67 +++++++ source/chapter07/arrfun1.cpp | 26 +++ source/chapter07/arrfun2.cpp | 37 ++++ source/chapter07/arrfun3.cpp | 78 ++++++++ source/chapter07/arrfun4.cpp | 31 +++ source/chapter07/arrobj.cpp | 40 ++++ source/chapter07/calling.cpp | 21 ++ source/chapter07/fun_ptr.cpp | 41 ++++ source/chapter07/lotto.cpp | 35 ++++ source/chapter07/protos.cpp | 32 ++++ source/chapter07/recur.cpp | 19 ++ source/chapter07/ruler.cpp | 37 ++++ source/chapter07/strctptr.cpp | 56 ++++++ source/chapter07/strgback.cpp | 33 ++++ source/chapter07/strgfun.cpp | 34 ++++ source/chapter07/structfun.cpp | 59 ++++++ source/chapter07/topfive.cpp | 28 +++ source/chapter07/travel.cpp | 46 +++++ source/chapter07/twoarg.cpp | 32 ++++ 21 files changed, 1164 insertions(+), 1 deletion(-) create mode 100644 source/chapter07/arfupt.cpp create mode 100644 source/chapter07/arfupt1.cpp create mode 100644 source/chapter07/arrfun1.cpp create mode 100644 source/chapter07/arrfun2.cpp create mode 100644 source/chapter07/arrfun3.cpp create mode 100644 source/chapter07/arrfun4.cpp create mode 100644 source/chapter07/arrobj.cpp create mode 100644 source/chapter07/calling.cpp create mode 100644 source/chapter07/fun_ptr.cpp create mode 100644 source/chapter07/lotto.cpp create mode 100644 source/chapter07/protos.cpp create mode 100644 source/chapter07/recur.cpp create mode 100644 source/chapter07/ruler.cpp create mode 100644 source/chapter07/strctptr.cpp create mode 100644 source/chapter07/strgback.cpp create mode 100644 source/chapter07/strgfun.cpp create mode 100644 source/chapter07/structfun.cpp create mode 100644 source/chapter07/topfive.cpp create mode 100644 source/chapter07/travel.cpp create mode 100644 source/chapter07/twoarg.cpp diff --git a/practice/booknotes.md b/practice/booknotes.md index 0c7a1cd..ada8fbe 100644 --- a/practice/booknotes.md +++ b/practice/booknotes.md @@ -838,7 +838,9 @@ pointername[i] becomes *(pointername + i) ``` 因此,在很多情况下,可以相同的方式使用指针名和数组名。也就是说,使用 `new` 来创建数组以及使用指针来访问不同的元素时,只要把指针当作数组名对待即可。 -> 【注意】数组名被解释为其第一个元素的地址,而对数组名应用地址运算符 时,得到的是整个数组的地址,区别很大。 +**【注意】数组名被解释为其第一个元素的地址,而对数组名应用地址运算符 时,得到的是整个数组的地址,区别很大。** + + ![image-20210723113548998](https://static.fungenomics.com/images/2021/07/image-20210723113548998.png) @@ -1740,14 +1742,351 @@ C++还提供了帮助决策的运算符。通过使用逻辑运算符(`&&`、` ## 7.1 函数基本知识 +自定义函数三要素: +- 函数定义; +- 函数原型; +- 调用函数。 + + +### 7.1.1 定义函数 + +函数定义通用格式: + +```Cpp +void functionName(parameterList) { + statement(s); + return; +} +``` +C++ 对于返回值的类型有一定的限制:**不能是数组**,但可以是其他任何类型——整数、浮点数、指针,甚至可以是结构体和对象。 + + +**C++ 函数返回值的原理是什么?** +首先,函数将返回值复制到指定的CPU寄存器或内存单元中将值返回;随后,调用程序将查看该内存单元。返回函数和调用函数必须就该内存单元中存储的数据的类型达成一致。函数原型将返回值 类型告知调用程序,而函数定义命令被调用函数应返回什么类型的数据。 + +![image-20210804133448677](https://static.fungenomics.com/images/2021/08/image-20210804133448677.png) + +### 7.1.2 函数原型和函数调用 + +1. 为什么需要函数原型 + +原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。举个例子: + +```Cpp +double volume = cube(side); +``` + +首先,原型告诉编译器,cube( )有一个double参数。如果程序没有 提供这样的参数,原型将让编译器能够捕获这种错误。其次,cube( )函 数完成计算后,将把返回值放置在指定的位置——可能是CPU寄存器, 也可能是内存中。然后调用函数(这里为main( ))将从这个位置取得返 回值。由于原型指出了cube( )的类型为double,因此编译器知道应检索 多少个字节以及如何解释它们。如果没有这些信息,编译器将只能进行 猜测,而编译器是不会这样做的。 + +为何编译器需要原型,它就不能在文件中进一步查找,以了解函数是如何定义的吗?这种方法的一个问题是效率不高。编译器在搜索文件的剩余部分时必须停止对 `main()` 的编译。一个更严重的问题是,函数甚至可能并不在文件中。C++允许将一个程序放 在多个文件中,单独编译这些文件,然后再将它们组合起来。在这种情 况下,编译器在编译`main()`时,可能无权访问函数代码。如果函数位于库中,情况也将如此。避免使用函数原型的唯一方法是,在首次使用函 数之前定义它,但这并不总是可行的。 + +2. 原型的语法 + +函数原型是一条语句,因此必须以分号结束。获得原型最简单的方 法是,复制函数定义中的函数头,并添加分号。 +函数原型不要求提供变量名,有类型列表就足够了。**通常,在原型的参数列表中,可以包括变量名,也可以不包括。原 型中的变量名相当于占位符,因此不必与函数定义中的变量名相同。** + +3. 原型的功能 + +它们可以极大地降低程序出错的几率。具体来说,原型 确保以下几点: + +- 编译器正确处理函数返回值; +- 编译器检查使用的参数数目是否正确; +- 编译器检查使用的参数类型是否正确,如果不正确,则转换为正确的类型。 + +![image-20210804144730761](https://static.fungenomics.com/images/2021/08/image-20210804144730761.png) + +仅当有意义时,原型化才会导致类型转换。例如,原型不会将整数转换为结构或指针。 + +在编译阶段进行的原型化被称为静态类型检查(static type checking)。 + +## 7.2 函数参数和按值传递 + +C++通常按值传递参数,这意味着将 数值参数传递给函数,而后者将其赋给一个新的变量。 + +```Cpp +double volume = cube(side); +``` + + + +![image-20210804145401521](https://static.fungenomics.com/images/2021/08/image-20210804145401521.png) + +side 是一个变量,被调用时,该函数将创建一个新的名为x的double变量,并将其初 始化为5。这样,cube( )执行的操作将不会影响main( )中的数据,因为 cube( )使用的是side的副本,而不是原来的数据。 + +**在函数中声明的变量(包括参数)是该函数私有的。在函数被调用 时,计算机将为这些变量分配内存;在函数结束时,计算机将释放这些 变量使用的内存**。这样的变量被称为局部变量,因为它们被限制在函数中。前面提到过,这样做有助于确保数据的完整性。这还意 味着,如果在main( )中声明了一个名为x的变量,同时在另一个函数中 也声明了一个名为x的变量,则它们将是两个完全不同的、毫无关系的变量。 + +![image-20210804145526933](https://static.fungenomics.com/images/2021/08/image-20210804145526933.png) + +### 7.2.1 多个参数 + +函数可以有多个参数。在调用函数时,只需使用逗号将这些参数分开即可。 + +![image-20210804150422862](https://static.fungenomics.com/images/2021/08/image-20210804150422862.png) + +它使用`cin>>ch`,而不是`cin.get(ch)`或`ch = cin.get()`来读取一个字符。这样做是有原因的。前面讲过,这两个`cin.get()` 函数读取所有的输入字符,包括空格和换行符,而 `cin>>` 跳过空格和换行符。当用户对程序提示作出响应时,必须在每行的最后按 `Enter` 键,以生成换行符。`cin>>ch` 方法可以轻松地**跳过这些换行符**,但当输入的下一个字符为数字时,`cin.get()` 将读取后面的换行符,虽然可以通过编程来避开这种麻烦,但比较简便的方法是像该程序那样使用 `cin`。 + +## 7.3 函数和数 + +需要将数组名作为参数传递给它,为使函数通用,而不限于特定长度的数组,还需要传递数组长度。 + +```Cpp +int sum_arr(int arr[], int n); +``` + +方括号指出 `arr` 是一个数组,而方括号为空则表明,可以将任何长度的数组传递给该函数。但实际情况并非如此:`arr`实际上并不是数组,**而是一个指针**,好消息是,在编写函数的其余部分时,可以将 `arr` 看作是数组。 + +### 7.3.1 函数如何使用指针来处理数组 + +在大多数情况下,C++和C语言一样,也将数组名视为指针。C++将数组名解释为其第一个元素的地址: + +```Cpp +cookies == &cookies[0]; // array name is the address of first element +``` + +首先,数组声明使用数组名来标记存储位置; 其次,对数组名使用sizeof将得到整个数组的长度(以字节为单位); 第三,正如第4章指出的,将地址运算符&用于数组名时,将返回整个数组的地址。 + +![image-20210804152348446](https://static.fungenomics.com/images/2021/08/image-20210804152348446.png) + +以下两个语句是恒等的: + +```Cpp +arr[i] == *(arr+i); // values in two notations +&arr[i] == arr + i; // addresses in two notations +``` + +### 7.3.2 将数组作为参数意味着什么 + +传递常规变量时,函数将使用该变量的拷 贝;但传递数组时,函数将使用原来的数组。实际上,这种区别并不违反C++按值传递的方法,`sum_arr()` 函数仍传递了一个值,这个值被赋给 一个新变量,但这个值是一个地址,而不是数组的内容。 + +![image-20210804154353723](https://static.fungenomics.com/images/2021/08/image-20210804154353723.png) + +**将数组地址作为参数可以节省复制整个数组所需的时间和内存**。如果数组很大,则使用拷贝的系统开销将非常大;程序不仅需要更多的计算机内存,还需要花费时间来复制大块的数据。另一方面,使用原始数据增加了破坏数据的风险。 + +`sum_arr(cookies+4, 4)` 和 `sum_arr(&cookies[4], 4)` 是等效的。 + +![image-20210804155245012](https://static.fungenomics.com/images/2021/08/image-20210804155245012.png) + + + +### 7.3.3 更多数组函数示 + +1.填充数组 + +2.显示数组及用const保护数组 + +创建显示数组内容的函数很简单。只需将数组名和填充的元素数目传递给函数,然后该函数使用循环来显示每个元素。然而,还有另一个问题——确保显示函数不修改原始数组。除非函数的目的就是修改传递 给它的数据,否则应避免发生这种情况。使用普通参数时,这种保护将自动实现,这是由于C++按值传递数据,而且函数使用数据的副本。然 而,接受数组名的函数将使用原始数据,这正是fill_array( )函数能够完 成其工作的原因。为防止函数无意中修改数组的内容,可在声明形参时 使用关键字`const`: + +```Cpp +void show_array(const double arr[], int n); +``` +`show_array()` 将数组视为只读数据。 + +3.修改数组 + +在这个例子中,对数组进行的第三项操作是将每个元素与同一个重新评估因子相乘。需要给函数传递3个参数:因子、数组和元素数目。 该 **函数不需要返回值**,因此其代码如下: + +```Cpp +void revalue(double r, double arr[], int n) { + for (int i=0; i < n; ++i) { + arr[i] *= r; + } +} +``` +由于这个函数将修改数组的值,因此在声明 `arr` 时,不能使用 `const`。 + +4.将上述代码组合起来 + + +前面已经讨论了与该示例相关的重要编程细节,因此这里回顾一下 整个过程。我们首先考虑的是通过数据类型和设计适当的函数来处理数 据,然后将这些函数组合成一个程序。有时也称为自下而上的程序设计 (bottom-up programming),因为设计过程从组件到整体进行。这种方 法非常适合于OOP——它首先强调的是数据表示和操纵。而传统的过程性编程倾向于从上而下的程序设计(top-down programming),首先指 定模块化设计方案,然后再研究细节。这两种方法都很有用,最终的产 品都是模块化程序。 + +6.数组处理函数的常用编写方式 + +假设要编写一个处理double数组的函数。如果该函数要修改数组, 其原型可能类似于下面这样: + +```Cpp +void f_modfiy(double arr[], int n); +``` + +如果函数不修改数组,其原型可能类似于下面这样: + +```Cpp +void _f_no_change(const double arr[], int n); +``` + +### 7.3.4 使用数组区间的函数 + +对于处理数组的C++函数,必须将数组中的数据种 类、数组的起始位置和数组中元素数量提交给它;传统的C/C++方法 是,将指向数组起始处的指针作为一个参数,将数组长度作为第二个参数(指针指出数组的位置和数据类型),这样便给函数提供了找到所有 数据所需的信息。 + +还有另一种给函数提供所需信息的方法,即指定元素区间 (range),这可以通过传递两个指针来完成:**一个指针标识数组的开头,另一个指针标识数组的尾部**。 + +![image-20210804170600111](https://static.fungenomics.com/images/2021/08/image-20210804170600111.png) + +它将 `pt` 设置为指向要处理的第一个元素(`begin`指向的元素)的指针,并将`*pt`(元素的值)加入到 `total` 中。然后,循环通过递增操作来更新 `pt`,使之指向下一个元素。只要 `pt` 不等于 `end`,这一过程就将继续下去。当 `pt` 等于 `end` 时,它将指向区间中最后一个元素后面的一个位置,此时循环将结束。 + +### 7.3.5 指针和 `const` + +将const用于指针有一些很微妙的地方,可以用两种不同的方式将 `const` 关键字用于指针。 第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值,第二种方法是将指针本身声明为常量,这样可以防止改变指针指向的位置。 + +![image-20210804171314723](https://static.fungenomics.com/images/2021/08/image-20210804171314723.png) + +![image-20210804171334907](https://static.fungenomics.com/images/2021/08/image-20210804171334907.png) + +现在来看一个微妙的问题。pt的声明并不意味着它指向的值实际上 就是一个常量,而只是意味着对pt而言,这个值是常量。例如,pt指向age,而age不是const。可以直接通过age变量来修改age的值,但不能使 用pt指针来修改它。 + +![image-20210804171429933](https://static.fungenomics.com/images/2021/08/image-20210804171429933.png) + +![image-20210804171526205](https://static.fungenomics.com/images/2021/08/image-20210804171526205.png) + +C++禁止第二种情况的原因很简单——如果将g_moon的地 址赋给pm,则可以使用pm来修改g_moon的值,这使得g_moon的const 状态很荒谬,因此C++禁止将const的地址赋给非const指针。 + +**假设有一个由const数据组成的数组则禁止将常量数组的地址赋给非常量指针将意味着不能将数组名作 为参数传递给使用非常量形参的函数。** + +只要条件允许,则应将指针形参声明为指向 `const` 的指针。 + +> **尽可能使用const**: +> 将指针参数声明为指向常量数据的指针有两条理由: +> - 这样可以避免由于无意间修改数据而导致的编程错误; +> - 使用const使得函数能够处理const和非const实参,否则将只能接受非const数据。 + +`const` 只能防止修改 `pt` 指向的值(这里为39),而不能防止修改 `pt` 的值。也就是说,可以将一个新地址赋给 `pt`。 + +第二种使用const的方式使得无法修改指针的值: + + + +![image-20210804172355209](https://static.fungenomics.com/images/2021/08/image-20210804172355209.png) + +![image-20210804172525799](https://static.fungenomics.com/images/2021/08/image-20210804172525799.png) + +通常,将指针作为函数参数来传递时,可以使用指向`const`的指针来 保护数据。 + +在该声明中使用 `const` 意味着 `show_array()` 不能修改传递给它的数组中的值。只要只有一层间接关系,就可以使用这种技术。例如,这里的数组元素是基本类型,但如果它们是指针或指向指针的指针,则不能使用 `const`。 + +## 7.4 函数和二维数组 + +数组名被视为其地 址,因此,相应的形参是一个指针,就像一维数组一样。 + +`Data` 是一个数组名,该数组有3个元素。第一个元素本身是一个数组,由4个 `int` 值组成。因此 `data` 的类型是指向由4个 `int` 组成的数组的指针,因此正确的原型如下: + +```Cpp +int sum(int (*ar2)[4], int size); +``` + +还有另外一种格式,这种格式与上述原型的含义完全相同,但可读性更强: + +```Cpp +int sum(int ar2[][4], int size); +``` + +对于二维数据,必须对指针 `ar2` 执行两次解除引用,才能得到数据。最简单的方法是使用方括号两次:`ar2[r][c]`。然而,如果不考虑难看的话,也可以使用运算符`*`两次: + +```Cpp +ar2[r][c] == *(*(ar2+r)+c); // 等效 +``` + +## 7.5 函数和C-风格字符串 + +C-风格字符串由一系列字符组成,以空值字符(`\0`)结尾。 + +### 7.5.1 将C-风格字符串作为参数的函数 + +![image-20210804204604444](https://static.fungenomics.com/images/2021/08/image-20210804204604444.png) + +假设要将字符串作为参数传递给函数,则表示字符串的方式有三 种: + +- `char` 数组; +- 用引号括起的字符串常量(也称字符串字面值); +- 被设置为字符串的地址的`char`指针。 + +但上述3种选择的类型都是`char`指针(准确地说是`char*`),因此可 以将其作为字符串处理函数的参数。 + +![image-20210804214230755](https://static.fungenomics.com/images/2021/08/image-20210804214230755.png) + +可以说是将字符串作为参数来传递,**但实际传递的是字符串第一个 字符的地址**。这意味着字符串函数原型应将其表示字符串的形参声明为 `char *` 类型。 + +C-风格字符串与常规 `char` 数组之间的一个重要区别是,字符串有内置的结束字符。这意味着不必将字符串长度作为参数传递给函数,而函数可以使用循环依次检查字符串中的每个字符,直到遇到结尾的空值字符为止。 + +> 不以空值字符结尾的char数组只是数组,而不是字符串。 + + +### 7.5.2 返回C-风格字符串的函数 + +函数无法返回一 个字符串,但可以返回字符串的地址,这样做的效率更高。 +这种设计(让函数返回一个指针,该指针指向new分配的内存)的缺点是,程序员必须记住使用 `delete`。 + +## 7.6 函数和结构体 + +**结构体变量的行为更接近于基本的单值变量**。也就是说,与数组不同,结构体将其数据组合成单个实体或数据对象,该实体被视为一个整体。前面讲过,可以将一个结构体赋给另外一个结构体。同样,也**可以按值传递结构体,就像普通变量那样**。在这种情况下,函数将使用原始结构体的副本。另外,函数也可以返回结构体。**与数组名就是数组第一个元素的地址不同的是,结构体名只是结构体的名称,要获得结构的地址,必须使用地址运算符 `&`**。 + +使用结构编程时,最直接的方式是像处理基本类型那样来处理结构体;也就是说,将结构体作为参数传递,并在需要时将结构体用作返回值使用。 + +### 7.6.1 传递和返回结构体 + +当结构体比较小时,按值传递结构体最合理。 + +### 7.6.2 另一个处理结构的函数示例 +### 7.6.3 传递结构体的地址 +假设要传递结构的地址而不是整个结构以节省时间和空间,则需要 重新编写前面的函数,使用指向结构的指针。首先来看一看如何重新编写 `show_polar()` 函数。需要修改三个地方: + +- 调用函数时,将结构的地址(`&pplace`)而不是结构本身(`pplace`) 传递给它; +- 将形参声明为指向 `polar` 的指针,即`polar *`类型。由于函数不应该修改结构,因此使用了 `const` 修饰符; +- 由于形参是指针而不是结构,因此应间接成员运算符(`->`),而不是成员运算符(句点)。 + +## 7.7 函数和 `string` 对象 + +可以将对象作为完整的实体进行传递,如果需要多个字符串,可以声明一个`string` 对象数组,而不是二维 `char` 数组。 + +## 7.8 函数与 `array` 对象 + +## 7.9 递归 +C++函数有一种有趣的特点——可 以调用自己,但不允许main()调用自己,这种功能称为递归。 + +> 我不喜欢递归,太耗资源,可读性还差。 + +### 7.9.1 包含一个递归调用的递归 +### 7.9.2 包含多个递归调用的递归 + + +## 7.10 函数指针 + +与数据项相似,函数也有地址。函数的地址是存储其机器语言代码 的内存的开始地址。对程序而言很有用,可以编写将另一个函数的地址作为参数的函数。 + +这样第一个函数将能够找到第二个函数,并运行 它。与直接调用另一个函数相比,这种方法很笨拙,但它允许在不同的 时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。 + +### 7.10.1 函数指针的基础知识 + +1.获取函数的地址 +获取函数的地址很简单:只要使用函数名(后面不跟参数)即可。。要将函数作为参数进行传递,必须传递函数名,要区分传递的是函数的地址还是函数的返回值。 + +2.声明函数指针 + +声明指向函数的指针时,必须指定指针指向的函数类型。意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。也就是说,声明应像函数原型那样指出有关函数的信息: + +![image-20210805084458826](https://static.fungenomics.com/images/2021/08/image-20210805084458826.png) + +3.使用指针来调用函数 + +![image-20210805084920592](https://static.fungenomics.com/images/2021/08/image-20210805084920592.png) + +> 为何pf和`(*pf`等价呢?一种学派认为,由于pf是函数指针,而 `*pf` 是函数,因此应将`(*pf)()`用作函数调用。另一种学派认为,由于函数名是指向该函数的指 针,指向函数的指针的行为应与函数名相似,因此应将pf( )用作函数调用使用。C++进行了折 衷——这2种方式都是正确的,或者至少是允许的,虽然它们在逻辑上是互相冲突的。在认为 这种折衷粗糙之前,应该想到,容忍逻辑上无法自圆其说的观点正是人类思维活动的特点。 +> +![image-20210805085227934](https://static.fungenomics.com/images/2021/08/image-20210805085227934.png) +可能看起来比较深奥,但指向函数指针数组的指针并不少见。实际上,类的虚方法实现通常都采用了这种技术(参见第13章)。 +### 7.10.4 使用typedef进行简化 +这里采用的方法是,将别名当做标识符进行声明,并在开头使用关键字 `typedef`。 +使用typedef可减少输入量,让您编写代码时不容易犯错,并让程序 更容易理解。 +### 7.11 总结 +函数是C++的编程模块。要使用函数,必须提供定义和原型,并调 用该函数。函数定义是实现函数功能的代码;函数原型描述了函数的接 口:传递给函数的值的数目和种类以及函数的返回类型。函数调用使得 程序将参数传递给函数,并执行函数的代码。 +C++提供了3种表示C-风格字符串的方法:字符数组、字符串常量 和字符串指针。它们的类型都是`char*`(`char`指针),因此被作为`char*`类型参数传递给函数。C++使用空值字符 `\0` 来结束字符串,因此字符 串函数检测空值字符来确定字符串的结尾。 +C++处理结构体的方式与基本类型完全相同,这意味着可以按值传递结构体,并将其用作函数返回类型。然而,如果结构体非常大,则传递结构体指针的效率将更高,同时函数能够使用原始数据。 diff --git a/source/chapter07/arfupt.cpp b/source/chapter07/arfupt.cpp new file mode 100644 index 0000000..565869d --- /dev/null +++ b/source/chapter07/arfupt.cpp @@ -0,0 +1,72 @@ +// arfupt.cpp -- an array of function pointers +#include +// various notations, same signatures +const double * f1(const double ar[], int n); +const double * f2(const double [], int); +const double * f3(const double *, int); + +int main() +{ + using namespace std; + double av[3] = {1112.3, 1542.6, 2227.9}; + + // pointer to a function + const double *(*p1)(const double *, int) = f1; + auto p2 = f2; // C++0x automatic type deduction + // pre-C++0x can use the following code instead + // const double *(*p2)(const double *, int) = f2; + cout << "Using pointers to functions:\n"; + cout << " Address Value\n"; + cout << (*p1)(av,3) << ": " << *(*p1)(av,3) << endl; + cout << p2(av,3) << ": " << *p2(av,3) << endl; + + // pa an array of pointers + // auto doesn't work with list initialization + const double *(*pa[3])(const double *, int) = {f1,f2,f3}; + // but it does work for initializing to a single value + // pb a pointer to first element of pa + auto pb = pa; + // pre-C++0x can use the following code instead + // const double *(**pb)(const double *, int) = pa; + cout << "\nUsing an array of pointers to functions:\n"; + cout << " Address Value\n"; + for (int i = 0; i < 3; i++) + cout << pa[i](av,3) << ": " << *pa[i](av,3) << endl; + cout << "\nUsing a pointer to a pointer to a function:\n"; + cout << " Address Value\n"; + for (int i = 0; i < 3; i++) + cout << pb[i](av,3) << ": " << *pb[i](av,3) << endl; + + // what about a pointer to an array of function pointers + cout << "\nUsing pointers to an array of pointers:\n"; + cout << " Address Value\n"; + // easy way to declare pc + auto pc = &pa; + // pre-C++0x can use the following code instead + // const double *(*(*pc)[3])(const double *, int) = &pa; + cout << (*pc)[0](av,3) << ": " << *(*pc)[0](av,3) << endl; + // hard way to declare pd + const double *(*(*pd)[3])(const double *, int) = &pa; + // store return value in pdb + const double * pdb = (*pd)[1](av,3); + cout << pdb << ": " << *pdb << endl; + // alternative notation + cout << (*(*pd)[2])(av,3) << ": " << *(*(*pd)[2])(av,3) << endl; + // cin.get(); + return 0; +} + +// some rather dull functions + +const double * f1(const double * ar, int n) +{ + return ar; +} +const double * f2(const double ar[], int n) +{ + return ar+1; +} +const double * f3(const double ar[], int n) +{ + return ar+2; +} diff --git a/source/chapter07/arfupt1.cpp b/source/chapter07/arfupt1.cpp new file mode 100644 index 0000000..59db302 --- /dev/null +++ b/source/chapter07/arfupt1.cpp @@ -0,0 +1,67 @@ +// arfupt.cpp -- an array of function pointers +#include +// various notations, same signatures +const double * f1(const double ar[], int n); +const double * f2(const double [], int); +const double * f3(const double *, int); + +int main() +{ + using namespace std; + double av[3] = {1112.3, 1542.6, 2227.9}; + + // pointer to a function + typedef const double *(*p_fun)(const double *, int); + p_fun p1 = f1; + auto p2 = f2; // C++0x automatic type deduction + cout << "Using pointers to functions:\n"; + cout << " Address Value\n"; + cout << (*p1)(av,3) << ": " << *(*p1)(av,3) << endl; + cout << p2(av,3) << ": " << *p2(av,3) << endl; + + // pa an array of pointers + p_fun pa[3] = {f1,f2,f3}; + // auto doesn't work with list initialization + // but it does work for initializing to a single value + // pb a pointer to first element of pa + auto pb = pa; + cout << "\nUsing an array of pointers to functions:\n"; + cout << " Address Value\n"; + for (int i = 0; i < 3; i++) + cout << pa[i](av,3) << ": " << *pa[i](av,3) << endl; + cout << "\nUsing a pointer to a pointer to a function:\n"; + cout << " Address Value\n"; + for (int i = 0; i < 3; i++) + cout << pb[i](av,3) << ": " << *pb[i](av,3) << endl; + + // what about a pointer to an array of function pointers + cout << "\nUsing pointers to an array of pointers:\n"; + cout << " Address Value\n"; + // easy way to declare pc + auto pc = &pa; + cout << (*pc)[0](av,3) << ": " << *(*pc)[0](av,3) << endl; + // slightly harder way to declare pd + p_fun (*pd)[3] = &pa; + // store return value in pdb + const double * pdb = (*pd)[1](av,3); + cout << pdb << ": " << *pdb << endl; + // alternative notation + cout << (*(*pd)[2])(av,3) << ": " << *(*(*pd)[2])(av,3) << endl; + // cin.get(); + return 0; +} + +// some rather dull functions + +const double * f1(const double * ar, int n) +{ + return ar; +} +const double * f2(const double ar[], int n) +{ + return ar+1; +} +const double * f3(const double ar[], int n) +{ + return ar+2; +} diff --git a/source/chapter07/arrfun1.cpp b/source/chapter07/arrfun1.cpp new file mode 100644 index 0000000..04d825c --- /dev/null +++ b/source/chapter07/arrfun1.cpp @@ -0,0 +1,26 @@ +// arrfun1.cpp -- functions with an array argument +#include +const int ArSize = 8; +int sum_arr(int arr[], int n); // prototype +int main() +{ + using namespace std; + int cookies[ArSize] = {1,2,4,8,16,32,64,128}; +// some systems require preceding int with static to +// enable array initialization + + int sum = sum_arr(cookies, ArSize); + cout << "Total cookies eaten: " << sum << "\n"; + // cin.get(); + return 0; +} + +// return the sum of an integer array +int sum_arr(int arr[], int n) +{ + int total = 0; + + for (int i = 0; i < n; i++) + total = total + arr[i]; + return total; +} diff --git a/source/chapter07/arrfun2.cpp b/source/chapter07/arrfun2.cpp new file mode 100644 index 0000000..c7a5b20 --- /dev/null +++ b/source/chapter07/arrfun2.cpp @@ -0,0 +1,37 @@ +// arrfun2.cpp -- functions with an array argument +#include +const int ArSize = 8; +int sum_arr(int arr[], int n); +// use std:: instead of using directive +int main() +{ + int cookies[ArSize] = {1,2,4,8,16,32,64,128}; +// some systems require preceding int with static to +// enable array initialization + + std::cout << cookies << " = array address, "; +// some systems require a type cast: unsigned (cookies) + + std::cout << sizeof cookies << " = sizeof cookies\n"; + int sum = sum_arr(cookies, ArSize); + std::cout << "Total cookies eaten: " << sum << std::endl; + sum = sum_arr(cookies, 3); // a lie + std::cout << "First three eaters ate " << sum << " cookies.\n"; + sum = sum_arr(cookies + 4, 4); // another lie + std::cout << "Last four eaters ate " << sum << " cookies.\n"; + // std::cin.get(); + return 0; +} + +// return the sum of an integer array +int sum_arr(int arr[], int n) +{ + int total = 0; + std::cout << arr << " = arr, "; +// some systems require a type cast: unsigned (arr) + + std::cout << sizeof arr << " = sizeof arr\n"; + for (int i = 0; i < n; i++) + total = total + arr[i]; + return total; +} diff --git a/source/chapter07/arrfun3.cpp b/source/chapter07/arrfun3.cpp new file mode 100644 index 0000000..d82877d --- /dev/null +++ b/source/chapter07/arrfun3.cpp @@ -0,0 +1,78 @@ +// arrfun3.cpp -- array functions and const +#include +const int Max = 5; + +// function prototypes +int fill_array(double ar[], int limit); +void show_array(const double ar[], int n); // don't change data +void revalue(double r, double ar[], int n); + +int main() +{ + using namespace std; + double properties[Max]; + + int size = fill_array(properties, Max); + show_array(properties, size); + if (size > 0) + { + cout << "Enter revaluation factor: "; + double factor; + while (!(cin >> factor)) // bad input + { + cin.clear(); + while (cin.get() != '\n') + continue; + cout << "Bad input; Please enter a number: "; + } + revalue(factor, properties, size); + show_array(properties, size); + } + cout << "Done.\n"; + // cin.get(); + // cin.get(); + return 0; +} + +int fill_array(double ar[], int limit) +{ + using namespace std; + double temp; + int i; + for (i = 0; i < limit; i++) + { + cout << "Enter value #" << (i + 1) << ": "; + cin >> temp; + if (!cin) // bad input + { + cin.clear(); + while (cin.get() != '\n') + continue; + cout << "Bad input; input process terminated.\n"; + break; + } + else if (temp < 0) // signal to terminate + break; + ar[i] = temp; + } + return i; +} + +// the following function can use, but not alter, +// the array whose address is ar +void show_array(const double ar[], int n) +{ + using namespace std; + for (int i = 0; i < n; i++) + { + cout << "Property #" << (i + 1) << ": $"; + cout << ar[i] << endl; + } +} + +// multiplies each element of ar[] by r +void revalue(double r, double ar[], int n) +{ + for (int i = 0; i < n; i++) + ar[i] *= r; +} diff --git a/source/chapter07/arrfun4.cpp b/source/chapter07/arrfun4.cpp new file mode 100644 index 0000000..361c87e --- /dev/null +++ b/source/chapter07/arrfun4.cpp @@ -0,0 +1,31 @@ +// arrfun4.cpp -- functions with an array range +#include +const int ArSize = 8; +int sum_arr(const int * begin, const int * end); +int main() +{ + using namespace std; + int cookies[ArSize] = {1,2,4,8,16,32,64,128}; +// some systems require preceding int with static to +// enable array initialization + + int sum = sum_arr(cookies, cookies + ArSize); + cout << "Total cookies eaten: " << sum << endl; + sum = sum_arr(cookies, cookies + 3); // first 3 elements + cout << "First three eaters ate " << sum << " cookies.\n"; + sum = sum_arr(cookies + 4, cookies + 8); // last 4 elements + cout << "Last four eaters ate " << sum << " cookies.\n"; + // cin.get(); + return 0; +} + +// return the sum of an integer array +int sum_arr(const int * begin, const int * end) +{ + const int * pt; + int total = 0; + + for (pt = begin; pt != end; pt++) + total = total + *pt; + return total; +} diff --git a/source/chapter07/arrobj.cpp b/source/chapter07/arrobj.cpp new file mode 100644 index 0000000..7fba284 --- /dev/null +++ b/source/chapter07/arrobj.cpp @@ -0,0 +1,40 @@ +//arrobj.cpp -- functions with array objects +#include +#include +#include +const int Seasons = 4; +const std::array Snames = + {"Spring", "Summer", "Fall", "Winter"}; + +void fill(std::array * pa); +void show(std::array da); +int main() +{ + std::array expenses; + fill(&expenses); + show(expenses); + // std::cin.get(); + // std::cin.get(); + return 0; +} + +void fill(std::array * pa) +{ + for (int i = 0; i < Seasons; i++) + { + std::cout << "Enter " << Snames[i] << " expenses: "; + std::cin >> (*pa)[i]; + } +} + +void show(std::array da) +{ + double total = 0.0; + std::cout << "\nEXPENSES\n"; + for (int i = 0; i < Seasons; i++) + { + std::cout << Snames[i] << ": $" << da[i] << '\n'; + total += da[i]; + } + std::cout << "Total: $" << total << '\n'; +} diff --git a/source/chapter07/calling.cpp b/source/chapter07/calling.cpp new file mode 100644 index 0000000..bd0f977 --- /dev/null +++ b/source/chapter07/calling.cpp @@ -0,0 +1,21 @@ +// calling.cpp -- defining, prototyping, and calling a function +#include + +void simple(); // function prototype + +int main() +{ + using namespace std; + cout << "main() will call the simple() function:\n"; + simple(); // function call + cout << "main() is finished with the simple() function.\n"; + // cin.get(); + return 0; +} + +// function definition +void simple() +{ + using namespace std; + cout << "I'm but a simple function.\n"; +} diff --git a/source/chapter07/fun_ptr.cpp b/source/chapter07/fun_ptr.cpp new file mode 100644 index 0000000..9dd0147 --- /dev/null +++ b/source/chapter07/fun_ptr.cpp @@ -0,0 +1,41 @@ +// fun_ptr.cpp -- pointers to functions +#include +double betsy(int); +double pam(int); + +// second argument is pointer to a type double function that +// takes a type int argument +void estimate(int lines, double (*pf)(int)); + +int main() +{ + using namespace std; + int code; + + cout << "How many lines of code do you need? "; + cin >> code; + cout << "Here's Betsy's estimate:\n"; + estimate(code, betsy); + cout << "Here's Pam's estimate:\n"; + estimate(code, pam); + // cin.get(); + // cin.get(); + return 0; +} + +double betsy(int lns) +{ + return 0.05 * lns; +} + +double pam(int lns) +{ + return 0.03 * lns + 0.0004 * lns * lns; +} + +void estimate(int lines, double (*pf)(int)) +{ + using namespace std; + cout << lines << " lines will take "; + cout << (*pf)(lines) << " hour(s)\n"; +} diff --git a/source/chapter07/lotto.cpp b/source/chapter07/lotto.cpp new file mode 100644 index 0000000..af4c437 --- /dev/null +++ b/source/chapter07/lotto.cpp @@ -0,0 +1,35 @@ +// lotto.cpp -- probability of winning +#include +// Note: some implementations require double instead of long double +long double probability(unsigned numbers, unsigned picks); +int main() +{ + using namespace std; + 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); // compute the odds + cout << " of winning.\n"; + cout << "Next two numbers (q to quit): "; + } + cout << "bye\n"; + // cin.get(); + // cin.get(); + return 0; +} + +// the following function calculates the probability of picking picks +// numbers correctly from numbers choices +long double probability(unsigned numbers, unsigned picks) +{ + long double result = 1.0; // here come some local variables + long double n; + unsigned p; + + for (n = numbers, p = picks; p > 0; n--, p--) + result = result * n / p ; + return result; +} diff --git a/source/chapter07/protos.cpp b/source/chapter07/protos.cpp new file mode 100644 index 0000000..11946c2 --- /dev/null +++ b/source/chapter07/protos.cpp @@ -0,0 +1,32 @@ +// protos.cpp -- using prototypes and function calls +#include +void cheers(int); // prototype: no return value +double cube(double x); // prototype: returns a double +int main() +{ + using namespace std; + cheers(5); // function call + cout << "Give me a number: "; + double side; + cin >> side; + double volume = cube(side); // function call + cout << "A " << side <<"-foot cube has a volume of "; + cout << volume << " cubic feet.\n"; + cheers(cube(2)); // prototype protection at work + // cin.get(); + // cin.get(); + return 0; +} + +void cheers(int n) +{ + using namespace std; + for (int i = 0; i < n; i++) + cout << "Cheers! "; + cout << endl; +} + +double cube(double x) +{ + return x * x * x; +} diff --git a/source/chapter07/recur.cpp b/source/chapter07/recur.cpp new file mode 100644 index 0000000..b80080a --- /dev/null +++ b/source/chapter07/recur.cpp @@ -0,0 +1,19 @@ +// recur.cpp -- using recursion +#include +void countdown(int n); + +int main() +{ + countdown(4); // call the recursive function + // std::cin.get(); + return 0; +} + +void countdown(int n) +{ + using namespace std; + cout << "Counting down ... " << n << endl; + if (n > 0) + countdown(n-1); // function calls itself + cout << n << ": Kaboom!\n"; +} diff --git a/source/chapter07/ruler.cpp b/source/chapter07/ruler.cpp new file mode 100644 index 0000000..b127325 --- /dev/null +++ b/source/chapter07/ruler.cpp @@ -0,0 +1,37 @@ +// ruler.cpp -- using recursion to subdivide a ruler +#include +const int Len = 66; +const int Divs = 6; +void subdivide(char ar[], int low, int high, int level); +int main() +{ + char ruler[Len]; + int i; + for (i = 1; i < Len - 2; i++) + ruler[i] = ' '; + ruler[Len - 1] = '\0'; + int max = Len - 2; + int min = 0; + ruler[min] = ruler[max] = '|'; + std::cout << ruler << std::endl; + for (i = 1; i <= Divs; i++) + { + subdivide(ruler,min,max, i); + std::cout << ruler << std::endl; + for (int j = 1; j < Len - 2; j++) + ruler[j] = ' '; // reset to blank ruler + } + // std::cin.get(); + + return 0; +} + +void subdivide(char ar[], int low, int high, int level) +{ + if (level == 0) + return; + int mid = (high + low) / 2; + ar[mid] = '|'; + subdivide(ar, low, mid, level - 1); + subdivide(ar, mid, high, level - 1); +} diff --git a/source/chapter07/strctptr.cpp b/source/chapter07/strctptr.cpp new file mode 100644 index 0000000..5e5a3c7 --- /dev/null +++ b/source/chapter07/strctptr.cpp @@ -0,0 +1,56 @@ +// strctptr.cpp -- functions with pointer to structure arguments +#include +#include + +// structure templates +struct polar +{ + double distance; // distance from origin + double angle; // direction from origin +}; +struct rect +{ + double x; // horizontal distance from origin + double y; // vertical distance from origin +}; + +// prototypes +void rect_to_polar(const rect * pxy, polar * pda); +void show_polar (const polar * pda); + +int main() +{ + using namespace std; + rect rplace; + polar pplace; + + cout << "Enter the x and y values: "; + while (cin >> rplace.x >> rplace.y) + { + rect_to_polar(&rplace, &pplace); // pass addresses + show_polar(&pplace); // pass address + cout << "Next two numbers (q to quit): "; + } + cout << "Done.\n"; + return 0; +} + +// show polar coordinates, converting angle to degrees +void show_polar (const polar * pda) +{ + using namespace std; + const double Rad_to_deg = 57.29577951; + + cout << "distance = " << pda->distance; + cout << ", angle = " << pda->angle * Rad_to_deg; + cout << " degrees\n"; +} + +// convert rectangular to polar coordinates +void rect_to_polar(const rect * pxy, polar * pda) +{ + using namespace std; + pda->distance = + sqrt(pxy->x * pxy->x + pxy->y * pxy->y); + pda->angle = atan2(pxy->y, pxy->x); +} diff --git a/source/chapter07/strgback.cpp b/source/chapter07/strgback.cpp new file mode 100644 index 0000000..2290086 --- /dev/null +++ b/source/chapter07/strgback.cpp @@ -0,0 +1,33 @@ +// strgback.cpp -- a function that returns a pointer to char +#include +char * buildstr(char c, int n); // prototype +int main() +{ + using namespace std; + int times; + char ch; + + cout << "Enter a character: "; + cin >> ch; + cout << "Enter an integer: "; + cin >> times; + char *ps = buildstr(ch, times); + cout << ps << endl; + delete [] ps; // free memory + ps = buildstr('+', 20); // reuse pointer + cout << ps << "-DONE-" << ps << endl; + delete [] ps; // free memory + // cin.get(); + // cin.get(); + return 0; +} + +// builds string made of n c characters +char * buildstr(char c, int n) +{ + char * pstr = new char [n + 1]; + pstr[n] = '\0'; // terminate string + while (n-- > 0) + pstr[n] = c; // fill rest of string + return pstr; +} diff --git a/source/chapter07/strgfun.cpp b/source/chapter07/strgfun.cpp new file mode 100644 index 0000000..f726ed7 --- /dev/null +++ b/source/chapter07/strgfun.cpp @@ -0,0 +1,34 @@ +// strgfun.cpp -- functions with a string argument +#include +unsigned int c_in_str(const char * str, char ch); +int main() +{ + using namespace std; + char mmm[15] = "minimum"; // string in an array +// some systems require preceding char with static to +// enable array initialization + + char *wail = "ululate"; // wail points to string + + unsigned int ms = c_in_str(mmm, 'm'); + unsigned int us = c_in_str(wail, 'u'); + cout << ms << " m characters in " << mmm << endl; + cout << us << " u characters in " << wail << endl; + // cin.get(); + return 0; +} + +// this function counts the number of ch characters +// in the string str +unsigned int c_in_str(const char * str, char ch) +{ + unsigned int count = 0; + + while (*str) // quit when *str is '\0' + { + if (*str == ch) + count++; + str++; // move pointer to next char + } + return count; +} diff --git a/source/chapter07/structfun.cpp b/source/chapter07/structfun.cpp new file mode 100644 index 0000000..9de0b87 --- /dev/null +++ b/source/chapter07/structfun.cpp @@ -0,0 +1,59 @@ +// strctfun.cpp -- functions with a structure argument +#include +#include + +// structure declarations +struct polar +{ + double distance; // distance from origin + double angle; // direction from origin +}; +struct rect +{ + double x; // horizontal distance from origin + double y; // vertical distance from origin +}; + +// prototypes +polar rect_to_polar(rect xypos); +void show_polar(polar dapos); + +int main() +{ + using namespace std; + rect rplace; + polar pplace; + + cout << "Enter the x and y values: "; + while (cin >> rplace.x >> rplace.y) // slick use of cin + { + pplace = rect_to_polar(rplace); + show_polar(pplace); + cout << "Next two numbers (q to quit): "; + } + cout << "Done.\n"; + return 0; +} + +// convert rectangular to polar coordinates +polar rect_to_polar(rect xypos) +{ + using namespace std; + polar answer; + + answer.distance = + sqrt( xypos.x * xypos.x + xypos.y * xypos.y); + answer.angle = atan2(xypos.y, xypos.x); + return answer; // returns a polar structure +} + +// show polar coordinates, converting angle to degrees +void show_polar (polar dapos) +{ + using namespace std; + const double Rad_to_deg = 57.29577951; + + cout << "distance = " << dapos.distance; + cout << ", angle = " << dapos.angle * Rad_to_deg; + cout << " degrees\n"; +} diff --git a/source/chapter07/topfive.cpp b/source/chapter07/topfive.cpp new file mode 100644 index 0000000..69b45f3 --- /dev/null +++ b/source/chapter07/topfive.cpp @@ -0,0 +1,28 @@ +// topfive.cpp -- handling an array of string objects +#include +#include +using namespace std; +const int SIZE = 5; +void display(const string sa[], int n); +int main() +{ + string list[SIZE]; // an array holding 5 string object + cout << "Enter your " << SIZE << " favorite astronomical sights:\n"; + for (int i = 0; i < SIZE; i++) + { + cout << i + 1 << ": "; + getline(cin,list[i]); + } + + cout << "Your list:\n"; + display(list, SIZE); + // cin.get(); + + return 0; +} + +void display(const string sa[], int n) +{ + for (int i = 0; i < n; i++) + cout << i + 1 << ": " << sa[i] << endl; +} diff --git a/source/chapter07/travel.cpp b/source/chapter07/travel.cpp new file mode 100644 index 0000000..c7bc4e5 --- /dev/null +++ b/source/chapter07/travel.cpp @@ -0,0 +1,46 @@ +// travel.cpp -- using structures with functions +#include +struct travel_time +{ + int hours; + int mins; +}; +const int Mins_per_hr = 60; + +travel_time sum(travel_time t1, travel_time t2); +void show_time(travel_time t); + +int main() +{ + using namespace std; + travel_time day1 = {5, 45}; // 5 hrs, 45 min + travel_time day2 = {4, 55}; // 4 hrs, 55 min + + travel_time trip = sum(day1, day2); + cout << "Two-day total: "; + show_time(trip); + + travel_time day3= {4, 32}; + cout << "Three-day total: "; + show_time(sum(trip, day3)); + // cin.get(); + + return 0; +} + +travel_time sum(travel_time t1, travel_time t2) +{ + travel_time total; + + total.mins = (t1.mins + t2.mins) % Mins_per_hr; + total.hours = t1.hours + t2.hours + + (t1.mins + t2.mins) / Mins_per_hr; + return total; +} + +void show_time(travel_time t) +{ + using namespace std; + cout << t.hours << " hours, " + << t.mins << " minutes\n"; +} diff --git a/source/chapter07/twoarg.cpp b/source/chapter07/twoarg.cpp new file mode 100644 index 0000000..02e09a9 --- /dev/null +++ b/source/chapter07/twoarg.cpp @@ -0,0 +1,32 @@ +// twoarg.cpp -- a function with 2 arguments +#include +using namespace std; +void n_chars(char, int); +int main() +{ + int times; + char ch; + + cout << "Enter a character: "; + cin >> ch; + while (ch != 'q') // q to quit + { + cout << "Enter an integer: "; + cin >> times; + n_chars(ch, times); // function with two arguments + cout << "\nEnter another character or press the" + " q-key to quit: "; + cin >> ch; + } + cout << "The value of times is " << times << ".\n"; + cout << "Bye\n"; + // cin.get(); + // cin.get(); + return 0; +} + +void n_chars(char c, int n) // displays c n times +{ + while (n-- > 0) // continue until n reaches 0 + cout << c; +}