C++两个函数可以相互递归吗_[C++ Primer plus 心得]7.函数—C++的编程模块

097d37baf3f2c2adbf4d9fcbb6b21039.png

本章内容包括:

  • 函数的基本知识
  • 函数原型
  • 按值传递函数参数
  • 设计处理数组的函数
  • 使用const指针参数
  • 设计处理文本字符串的函数
  • 设计处理结构的函数
  • 设计处理string对象的函数
  • 调用自身的函数(递归)
  • 指向函数的指针

7.1 函数基本知识:

要使用C++函数,要完成工作:

1. 提供函数基本知识;

2. 提供函数原型;

3. 调用函数

库函数是已经定义和编译好的函数,同时可以使用标准库头文件提供其原型

例:标准头文件 cstring 中包含了 strlen() 和其它一些字符串相关的函数原型

定义函数的模板:

typename functionName(parameterList){ statements return value;}

对于返回值的函数,必须使用return语句返回。可以是常量、变量或者是表达式。其结果的类型只能为typename,若不是,会进行强制类型转换。

C++对返回值的类型一定的限制:不能使数组,但可以是其他任何类型——整数、浮点数、指针甚至可以是结构和对象!(数组可以作为结构或者对象的部分来返回)

通常,函数通过将返回值复制到指定的cpu寄存器或者内存单元来将其返回。随后,调用程序将查看这个内存单元。

函数在执行返回语句后结束,如果函数中包含多条返回语句,执行第一条后结束。

7.1.1函数原型 和函数调用

函数原型描述了函数到编译器的接口,将函数返回值的类型及参数类型和数量告诉编译器;

double cube ( double x );   or  double cube ( double );   

原型的功能:

编译器正确处理函数返回值;

编译器检查使用的参数数目是否正确;

编译器检查使用参数类型是否正确,如果不正确,转换为正确的类型(可能的话)

7.2函数参数和值传递

例:

double volume=cube(side);double cube(double x);

调用函数时,该函数将创建一个名为x的变量,并初始化为side的值。函数cube()接收的是side的副本而不是side。用于接收传递值的变量被称为形参。传递给函数的值被称为实参。处于简化的目的,C++使用参数来表示实参,使用参量来表示形参。

在函数中声明的变量(包括参数是该函数私有的,在函数被调用时,计算机将为这些变量分配内存,函数结束时,计算机将释放这些内存,这些变量被称为局部变量。

7.3 函数和数组

7.3.1 使用数组作为函数的参数:

例:int sum(int a[],int n)

注:在参数传递时,程序将数组的位置(指针传递给函数,并不是整个数组)。

C++和C语言一样,将数组名视为指针,C++将数组名解释为第一个元素的地址。

例外:

  • 数组声明使用数组名来标记存储位置
  • 对数组名使用sizeof()将得到整个数组的长度
  • 将地址运算符&用于数组名时,将返回整个数组的地址。

函数传递数组时,将数组的位置(地址)、包含的元素种类(类型)以及元素的数目(n变量)提交给函数;

函数传递常规变量时,函数将使用变量的拷贝,但使用数组时,函数使用原来的数组;

可以在被传递函数中,数组参数使用 const 限定符,保证原始数组数据不被修改(只读传入)

int show_array ( const double arr[ ],int limit ); // 函数原型

7.3.2 自下而上的程序设计:

   通过数据类型和设计适当的函数来处理数据,然后将这些函数组合成一个程序;

   适合于OOP——它首先强调的数据表示和操纵

7.3.3 使用数组区间的函数:

   对于处理数组的C++函数,必须将处理数组中的种类、数组的起始位置和数组中元素数量交给函数:

1. 将数组的起始处的指针作为一个参数,将数组的长度作为第二个参数;

2. 指定元素区间,通过传递两个指针完成,一个标识数组开头,一个标识数组尾部

eg: sum = sum_arr ( arr, arr+3 ); // 函数调用,实参传入地址区间 int sum ( const int* begin,const int* end );// 函数头,形参为两个指向数组类型的指针(int*)

7.3.4 指针和 const:

   const用于指针一些很微妙的地方。可以使用两种方式将关键字const用于指针。第一种方法是让指针指向一个常量对象,这样能够防止使用该指针修改所指向的值,第二种方法是将指针变量本身声明为常量,这样能够防止指针指向的位置。

可以使用两种不同的方式将 const 关键字用于指针:

1. 让指针指向一个常量对象,防止使用该指针修改所指向的值;

2. 将指针本身声明为常量

声明一个指向常量的指针:

int age = 39;const int * pt = &age;

声明中 pt 指向一个 const int 因此不能使用 pt 修改这个值—— *pt 为常量不能修改

还可以将 const 变量的地址赋给指向const的指针,不能将 const 的地址赋给常规的指针

记住:如果数据类型不是指针,可以将 const 数据或非 const 数据的地址指向 const 指针

不能将 const 数据赋给非 const 指针

 int * const finger = &sloth:

指针 finger 本身被声明为 const,使得 finger 只能指向 sloth,但允许使用 finger 来修改 sloth 的值

7.4 函数和二维数组

数组作为参数的函数,数组名被视为地址,相应的形参应为一个指针;

int data[3][4] = { {1,2,3,4}, {9,8,7,6}, {2,4,6,8} }; // 声明int tatal = sum(data, 3); // 调用

   两种函数原型(形参的形式):

int sum (int (*ar2) [4],int size ); // 声明一个由4个指向 int 的指针组成的数组,括号不能省int sum (int ar2[ ][4],int size); // 可读性更强

在函数定义中使用二维数组,最简单的办法是将 ar2 看作一个二维数组的名称

ar2 实际上是一个指针,必须对 ar2 执行两次解除引用才能得到数据:

最简单的方式:ar2[r][c];ar2[r][c] = *(*(ar2 + r) + c); // same thing

7.5 函数和C-风格字符串

C-风格字符串由一系列字符组成,以空值字符结尾,作为参数时意味着传递地址。

表示字符串的方式有3种

1. char 数组2. 用引括号起的字符串常量3. 被设置为字符串的地址的 char 指针

将字符串作为参数来传递,实际传递的是字符串的第一个字符的地址,形参声明应为 char*:

int c_in_str (const char * str,char ch); // 使用指针表示法int c_in_str (const char str[],char ch); // 也可以使用数组表示法

处理字符串中字符的标准方式:

while (*str) // until *stt == '0'{ statement; str++      // 将指针增加一个字节}

7.5.2 返回C-风格字符串的函数

函数声明:

char *buildstr(char c,int n);

调用:

char *ps=buildstr(ch,times); //字符串指针的初始化,ch='a',times=10;

函数:

char *buildstr(char c,int n){ char *pstr=new char [n+1]; pstr[n]='0'; while(n-->0) pstr[n]=c; return pstr;}

7.6 函数和结构

7.6.1 传递和返回结构

结构定义:

struct student{ char name[20]; int age;};

函数声明:student sum(const student s1,student s2);

函数调用:student s=sum(s1,s2); //初始化

函数:

student sum(const student s1,student s2){ student s; s.age=s1.age+s2.age; strcpy(s.name,s1.name); strcat(s.name,s2.name); return s;}

照上述操作,参数传递的是整个结构,如果结构很大,效率很低。

7.6.2传递结构的地址

如果要传递结构的地址的话,需要修改三个地方,以上述函数为例:

  • 调用函数时,将结构的地址(&s而不是结构本身(s)传递给它
  • 将形参声明为指向student的指针,即*student。
  • 由于形参是指针而不是结构,因此使用间接成员运算符(->)而不是成员运算符(句点。

修改后的程序如下:

结构定义:

struct student{ char name[20]; int age;};

函数声明:student sum(const student *s1,const student *s2);

函数调用:student s=sum(s1,s2); //初始化

函数:

student sum(const student s1,const student s2){ student s; s->age=s1->age+s2->age; strcpy(s->name,s1->name); strcat(s->name,s2->name); return s;}

7.7函数和 string 对象:

   C++如果需要多个字符串,可以声明一个 string 对象数组,而不是二维 char 数组;

   以下例程声明了一个 string 对象数组,并将该数组传递给一个函数以显示其内容:

#include#includeusing namespace std;const int SIZE = 5;void display(const string list[], int n);int main(){ string list[SIZE];    // 声明 string 数组,每一个元素为一个 string 对象 cout << "Enter your " << SIZE << " favotite astronomical sights: "; for (int i = 0; i < SIZE; i++) { cout << i + 1 << ": ";     getline(cin, list[i]); // 读取一个字符串  } cout << "Your list: "; display(list, SIZE); return 0;}void display(const string list[], int n){ for (int i = 0; i < n; i++) cout << list[i] << endl;}

该例程中,除了函数 getline() 外,程序像对待内置类型(int)一样对待 string 对象

7.8函数与array对象

函数声明:

void show(std::array da); //da是数组名。

或者:

void show(std::array *pa); //pa是指针

注意,第一种的参数传递是全部复制,第二种只是传递地址,效率较高。

7.9 递归

C++函数一个有趣的特点:可以调用自己(C++不允许main函数自己调用自己,C语言可行),这种功能被称为递归。

包含一个递归调用的递归

如下:

void recurs(argumentlist){ statements1 if (test) recurs(arguments) statements2}

test最终为false,程序断开。

如果recurs进行了5次递归调用,statements1将函数调用顺序执行5次,statements2将函数调用相反的顺序执行5次。

注意,每个递归调用都会创建自己的一套变量,所以程序在到达第五次调用的时候会有五个独立的变量,其中每个变量的值都不同。

7.9.1 包含多个递归调用的递归

递归方法时被称为分而治之策略。

现演示多个递归调用的递归的实例:

//ruler.cpp 使用递归来标定直尺的中点

#includeconst 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

7.10 函数指针

可以编写将另一个函数的地址作为参数的函数;

这种方法与调用函数相比,允许在不同的时间传递不同的函数地址;

7.10.1 函数指针基础知识:

获取函数地址;

声明函数的指针;

使用函数指针来调用函数

1. 获取函数地址

只要使用函数名即可,如果 think() 是一个函数,则 think 就是该函数的地址

要区分函数的地址和函数的返回值:

process ( think );  // 参数为函数地址,使得函数 process 能够在函数内部调用 think() 函数thought ( think() ); // 参数为函数的返回值,先调用 think() 函数,其返回值传给 thought

2. 声明函数指针

声明函数指针时,必须指定指针指向的函数类型:

double pam ( int ); // 原型double ( *pf ) ( int ); // 指针类型声明,将 pam 替换为了 (*pf),(*pf)是函数,pf 就是函数指针pf = pam;      // 将相应的函数地址赋给指针

提示: 要声明指向特定类型的函数指针,可以先编写这种函数的原型,然后用(*pf)替换函数名

注意: 函数地址赋给函数指针时,特征标和返回返回类型必须相同

3. 使用指针来调用函数

void estimate ( int lines,double (*pf) (int) );  // 函数原型estimate ( 50, pam);            // 让 estime() 使用 pam() 函数(*pf) 扮演的角色与函数名相同,使用(*pf)时,只需要将它看成函数名double pam ( int );double (*pf) (int);pf = pam;       // 函数指针指向函数 pam()double x = pam (4);  // 使用函数名调用函数pam()double y = (*pf) (5);  // 使用函数指针调用函数pam()double y = pf(5);    // C++允许像使用函数名那样使用 pf

7.10.2 深入探讨函数指针

例:声明一个函数指针:

const double *(*p1)(const double *,int);

也可在声明的同时进行初始化:

const double *(*p1)(const double *,int)=f1;

也可使用C++11的自动类型推断功能

auto p2=f2;

可完成函数指针p2的定义,代码大大简化,但是仅能用于单值,不能用于列表。

函数指针数组:

定义与初始化:

const double * (*pa[3])(const double *,int)={f1,f2,f3};

7.10.3 使用typedef进行简化

可使用typedef进行简化

typedef可创建类型别名

typedef double real; //创建double的别名real

typedef const double *(*p_fun)(const double *,int);

使用:p_fun p1=f1;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值