以下是《C++ Primer Plus》中第七章的内容:
使用C++函数的3个步骤:
提供函数定义
提供函数原型
调用函数
7.1.1 定义函数
函数分成两类:没有返回值的函数、有返回值的函数。
return返回语句标记了函数的结尾;否则,函数将在右花括号处结束。
对于有返回值的函数,必须使用返回语句,以便将值返回给被调用函数。
C++对于返回值的类型有一定的限制:不能是数组,但可以是其他任何类型。
7.1.2 函数原型和函数调用
为什么需要函数原型:原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型以及参数的类型和数量告诉编译器。其次,函数完成计算后,将返回值放置在指定的位置——可能是CPU寄存器、也可能是内存中。然后调用函数将从这个位置获取返回值。原型指出了该函数的类型为double,因此编译器指导应检索多少个字节以及如何解释它们。
编译器需要原型,这样也可以提高效率。
原型的语法:函数原型是一条语句,因此必须以分号结束。原型不需要提供变量名,有类型列表就足够了。C++中原型是必不可少的。
原型的功能:原型可以帮助编译器完成许多工作;它可以极大地降低程序出错的几率。编译阶段进行的原型化被称为静态类型检查。
========================================
7.2 函数参数和按值传递
接收传递值的变量被称为形参;传递给函数的值被称为实参。
C++标准使用参数(argument)来表示实参,使用参量(parameter)来表示形参。参数传递将参数传递给参量。形参是实参的副本。
函数声明中的函数变量(包括参数)是函数私有的。在函数被调用时,计算机将为这些变量分配内存。函数结束时,计算机将释放这些变量使用的内存。这样的变量被称为局部变量,因为它们被限制在函数中。这样有助于确保数据的完整性。这样的变量也被称为自动变量,因为它们在程序执行过程中是自动被分配和释放的。
7.2.1 多个参数
函数可以有多个参数,只需使用逗号将这些参数分开即可。
只需添加分号就可得到该函数的原型;
提供变量名将使原型更易于理解。
7.2.2 另外一个接受两个参数的函数
=====================================================
7.3 函数和数组
函数是处理更复杂的类型的关键。
7.3.1 函数如何使用指针来处理数组
C++和C语言一样,也将数组名视为指针。C++将数组名解释为其第一个元素的地址。
有个例外:使用取址运算符&用于数组名时,将返回整个数组的地址。
int * arr == int arr [ ];
指针也可以用方括号表示法来访问数组元素。
记住:指针加1,实际上是加上了一个与指针指向的类型的长度相等的值。
对于遍历数组而言,使用指针加法和数组下标时等效的。
7.3.2 将数组作为参数意味着什么
实际上并没有把数组的内容传递给函数,而是将数组的位置信息,长度,包含元素种类告诉了函数。函数便可使用原来的数组。传递的值是数组的地址。将数组地址作为参数可以节省复制整个数组所需的时间和内存。但是使用原始数据增加了破坏数据的风险。但是ANSI C和C++中的const限定符提供了解决这种问题的办法。
7.3.3 更多数组函数示例
构思程序时将:存储属性和对数据的操作结合起来。
7.3.4 使用数组区间的函数
传统的C/C++方法处理数组的函数是:将指向数组起始处的指针作为一个参数,将数组长度作为第二个参数,这样便给函数提供了找到所有数据所需的信息。
另一种给函数提供所需信息的方法:即指定元素区间。可以通过传递两个指针来完成。一个指针标识数组的开头,一个指针标识数组的尾部。
end – begin 是一个整数值,等于数组的元素数目。
7.3.5 指针和const
有两种将const和指针结合使用的方法:
让指针指向一个常量对象:可以防止使用该指针来修改所指向的值。(指针指向的值)
int age =39;
const int * pt = &age;
微妙的地方是,pt的声明并不意味着它指向的值实际上就是一个常量。而只是对pt而言,这个值是常量。age不是const,可以直接通过age变量来修改age的值。
将指针本身声明为常量:可以防止修改指针指向的位置。(指针的值)
int * const finger = &sloth;
将指针作为函数参数来传递时,可以使用指向const的指针来保护数据。
====================================================
7.4 函数和二维数组
将二维数组作为参数的函数:
int sum(int ar2[ ] [4], int size);
int sum(int (* ar2)[4], int size);
====================================================
7.5 函数和C-风格字符串
C风格字符串由一系列字符组成:以空字符结尾,有关设计数组函数的知识也适用于字符串函数。
7.5.1 将C-风格字符串作为参数的函数
表示字符串的方式有3种:
char数组;用引号括起来的字符串常量;备设置为字符串的地址的char指针;
但上述3种选择的类型都是char指针,因此可以将其作为字符串处理函数的函数;
将字符串作为参数来传递,但实际传递的是字符串的第一个字符的地址。字符串函数原型应将其表示字符串的形参声明为char *类型。
C风格字符串与常规char数组之间的一个重要区别:字符串有内置的结束字符。这意味着不必将字符串长度作为参数传递给函数。而函数可以使用循环依次检查字符串中的每个字符,直到遇到结尾的空值字符为止。
7.5.2 返回C-风格字符串的函数
函数无法返回一个字符串,但是可以返回字符串的地址,这样做效率更高。
====================================================
7.6 函数和结构
为结构编写函数比为数组编写函数要简单得多。结构变量的行为更接近于基本的单值变量。
注意:结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&。
使用结构编程时,最直接的方式是像处理基本类型那样来处理结构;将结构作为参数传递,并在需要时将结构用作返回值使用。
按值传递结构有一个缺点。如果结构非常大,则复制结构将增加内存要求,降低系统运行的速度。处于这些原因,许多程序员倾向于传递结构的地址,然后使用指针来访问结构的内容。
还有第三种选择:按引用传递。
7.6.1 传递和返回结构
7.6.2 另一个处理结构的函数示例
7.6.3 传递结构的地址
传递结构的地址而不是整个结构以节省时间和空间,使用指向结构的指针。
注意点:
调用函数时,将结构的地址(&pplace)而不是结构本身(pplace)传递给它。
将形参声明为指向polar的指针,即polar *类型。由于函数不应该修改结构,因此使用了const修饰符。
由于形参是指针而不是结构,因此应使用间接成员运算符(->),而不是成员运算符(句点)。
====================================================
7.7 函数和string对象
功能上C风格字符串与string对象的用途几乎相同。但是string对象与结构更相似。例如:可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体传递给函数。如果需要多个字符串,可以声明一个string对象数组。
====================================================
7.8 函数与array对象
按值传递对象给函数,函数处理的是原始对象的副本。
也可以传递指向对象的指针,这让函数能够操作原始对象。(修改对象,按地址传递)
按值传递(拷贝副本)、占用内存空间;
传递地址;但是代码看起来更复杂;
引用传递;-->解决了效率和表示法两方面的问题;
====================================================
7.9 递归
函数调用自己,被称为递归。
7.9.1 包含一个递归调用的递归
7.9.2 包含多个递归调用的递归
====================================================
7.10 函数指针
函数也有地址,函数的地址是存储器机器语言代码的内存的开始地址。通常这个地址对用户而言既不重要也没有什么用处。对程序而言,却很有用,可以编写将另一个函数的地址作为参数的函数。
7.10.1 函数指针的基础知识
获取函数地址;
函数名及函数的地址;
声明函数指针;
声明应该像函数原型那样指出有关函数的信息。
double pam(int); //函数的prototype
double (*pf)(int); //声明指向函数的指针,包含指向函数的返回类型、函数的特征标;
使用函数指针来调用函数;
对于(*pf),只需将它看做是函数名即可。
double pam(int);
double (*pf)(int); //声明函数指针
pf =pam; //将pam函数赋给pf函数指针,
double x = pam(4);
double y =(*pf)(5); //这种格式虽然不太好看,但是给出了强有力的提示,代码正在使用函数指针
double y = pf(5); //这个格式就和函数差不多;
7.10.2 函数指针示例
7.10.3 深入探讨函数指针
7.10.4 使用typedef进行简化
关键字typedef让您能够创建类型别名;
使用typedef可减少输入量,让您编写代码时不容易犯错。
例子:
typedef const double *(*p_fun)(const double *, int); //p_fun现在就是函数指针,或者可以认为是一种类型
p_fun p1 = f1; //现在p1是指向f1的函数指针
====================================================
7.11 总结
函数是C++的编程模块。要使用函数,必须提供定义和原型,并调用该函数。函数定义是实现函数功能的代码;函数原型描述了函数的接口:传递给函数的值的数目和种类以及函数的返回类型;函数调用使得程序将参数传递给函数,并执行函数的代码。
在默认情况下,C++函数按值传递参数。这意味着函数定义中的形参是新的变量,它们被初始化为函数调用所提供的值。C++函数通过使用拷贝,保护了原始数据的完整性。
C++将数组名参数视为数组第一个元素的地址。从技术上讲,这仍然是按值传递的,因为指针是原始地址的拷贝,但函数将使用指针来访问原始数组的内容。当且仅当声明函数的形参时,下面两个声明才是等价的。
typename arr[ ];
typename * arr;
C++提供了3中表示C-风格字符串的方法:字符数组、字符串常量、字符串指针;它们的类型都是char *;
C++提供了string类,用于表示字符串。
C++处理结构的方式与基本类型完全相同,这意味着可以按值传递结构,并将其用作函数返回类型。
C++函数可以是递归的;
C++函数名与函数地址作用相同。