• C++ Primer Plus学习:第七章


    C++入门第七章:函数-C++的编程模块

    • 函数的基本知识

    要使用C++函数,必须完成如下工作:

    • 提供函数定义
    • 提供函数原型
    • 调用函数

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

    定义函数的模板:

    typename functionName(parameterList)

    {

    statements

    return value;

    }

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

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

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

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

    函数原型

    函数原型描述了函数到编译器的接口,也就是说,它将函数返回至的类型(若有)以及参数的类型和数量告诉编译器。

    定义原型最简单的方法就是复制定义函数的函数头并加上分号。当然,定义原型并不需要变量名。

    • 函数参数和按值传递

    例:

    double volume=cube(side);

    double cube(double x);

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

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

    • 函数和数组

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

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

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

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

    例外:

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

    使用函数填充数组

    函数示例如下:

    函数原型:

    int fill_array(double a[],int limit);

    函数的核心代码如下:

    int fill_array(double a[],int limit)

    {

    using namespace std;

    double temp;

    int i;

    for (i=0;i<limit;i++)

    {

    cin>>temp;

    if (!cin)

    {

    cin.clear();

    while(cin.get()!=' ')

    continue;

    cout<<"错误";

    break;

    }

    else if (temp<0)

    break;

    a[i]=temp;

    }

    return i;

    }

    显示数组及用const保护数组

    使用函数时,必须确保函数不修改原始数组,除非函数的目的就是修改传递给它的数据。

    接受数组名的函数将使用原始数据。

    为防止函数无意中修改数组的内容,声明形参时使用关键字:

    例:void show_array(const double a[],int n)

    上述函数并不是意味着不能使用a修改该数据,而是意味这将数组a[]视为只读数组,如果修改数组中的元素,编译器会报错。

    使用数组区间的函数

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

    还有另一种给函数提供所需信息的方法,即指定元素区间(range),这可以通过传递两个指针来完成,一个指针标识数组的开头,另一指针标识数组的结尾。STL使用"超尾"来指定区间,也就是说标识数组结尾的参数将会是指向数组结尾的指针。

    例:

    函数原型:int sum(const int *begin,const int *end);

    调用:sum1=sum(a,a+3); //a的定义:int a[3]

    函数:

    int sum(const int *begin,const int *end)

    {

    const int *pt;

    int total=0;

    for (pt=begin;pt!=end;pt++)

    total+=pt;

    return total;

    }

    指针和const

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

    例:

    int age;

    const int *pt=&age;

    pt而言,age 是常量,不可使用*pt修改age的值。

    还有一种情况:

    const int age;

    const int *pt=&age;

    变量和指针均为const类型。

    C++规定,禁止将const地址赋给非const指针。如果非要这样做,可以使用强制类型转换。

    即,以下操作时不允许的:

    const int a[3]={1,2,3};

    有一函数的原型:int sum(int a[],int n)

    上述函数调用试图将const指针赋给非const变量,编译器将禁止这种函数的调用。

    建议:尽量使用const

    理由:

    • 避免由于无意间修改数据造成编程错误
    • 使得const变量能够处理const和非const实参,否则只能接受非const数据。

    如果条件允许,则应将指针形参声明为指向const的指针。

    示例:

    int age=4;

    int * const pa=&age;

    上述声明意味着可以使用*pa来修改age的值,但不允许pa指向另一个位置。

    当然,如果使用如下定义:

    const int * const pa=&age;

    则表示pa只能指向age,而且*pa不能修改age的值。

    • 函数和二维数组

    正确的声明:

    int sum(int a[][3],int size); //其中,int a[4][3];

    上述声明指出,a是指针而不是数组,指针类型还指出,它是由三个指向int类型的指针组成的数组。

    指针类型制定了行数,

    调用函数:

    sum(a,3); //计算所有a的值

    或者:sum(a+2,1); //计算最后一行的值

    如果我们声明一个二维数组:int a[100][20];

    a[r][c]=*(*(a+r)+c);完全等价

    其中,

    a代表指向第一行100个数组的指针。

    a+r代表指向第r+1行数组的指针(指向r+1行首元素地址的指针)。

    *(a+r)代表第r+1行。(指向r+1行首元素的指针)

    *(a+r)+c代表指向r+1行第c+1个元素的指针。

    *(*(a+r)+c)代表r+1行第c+1个元素。

    5 函数和C-风格字符串

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

    若要将字符串作为参数传递给函数,表示字符串的方式有三种:

    • char数组
    • 使用引号括起的字符串常量(字符串字面值)
    • 被设置为字符串地址的char指针

    声明方式:int str(const char c[],char ch);

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

    while(*str)

    {

    statements

    str++;

    }

    返回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]='';

    while(n-->0)

    pstr[n]=c;

    return pstr;

    }

    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;

    }

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

    传递结构的地址

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

    • 调用函数时,将结构的地址(&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;

    }

    1. 函数和string对象

    和普通的数据类型的操作相同。

    例:

    void display(const string s[],int n)

    {

    for (int i=0;i<n;i++)

    cout<<i+1<<":"<<sa[i]<<endl;

    }

    1. 函数与array对象

    函数声明:

    void show(std::array<double,4> da); //da是数组名。

    或者:

    void show(std::array<double,4> *pa); //pa是指针

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

    1. 递归

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

    包含一个递归调用的递归

    如下:

    void recurs(argumentlist)

    {

    statements1

    if (test)

    recurs(arguments)

    statements2

    }

    test最终为false,程序断开。

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

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

    包含多个递归调用的递归

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

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

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

    #include<iostream>

    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]='';

    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=i;j<Len-2;j++)

    ruler[j]=' ';

    }

    return 0;

    }

    void subdivide(char a[],int low,int high,int level)

    {

    if (level==0)

    return 0;

    int mid=(high+low)/2;

    ar[mid]='|';

    subdivide(ar,low,mid,level-1);

    subdivide(ar,mid,high,level-1);

    }

    1. 函数指针

    与数据项类似,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。对程序而言,这些地址很有用,例如,可以编写将一个函数的地址作为参数的函数。它允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。

    函数指针的基础知识

    例子:设计一个名为estimate()的函数,估算编写指定行数代码所需要的时间,并且希望不同程序员使用该函数。对于用户看来说,estimate()中一部分代码是相同的,但函数允许每个程序员提供自己的算法来估计时间。为此,我们采用将不同程序员使用的算法函数的地址作为参数传递给estimate(),为此,必须完成以下工作:

    • 获取函数地址
    • 声明一个函数指针
    • 使用函数指针来调用函数

    获取函数地址:函数名即为函数的地址。

    例如:函数int sum(int a,int b)的地址为sum

    声明函数指针:声明时应指出函数的返回值类型以及函数的特征表(参数列表)。

    以函数int sum(int a,int b)为例,应按以下方法声明指向该函数的指针:

    int (*ps)(int,int);其中,ps为函数指针。

    ps=sum;//表示将ps指向函数sum

    使用指针调用函数的两种方法:

    函数int sum(int a,int b)为例,

    int (*ps)(int,int); //ps为函数指针。

    ps=sum; //表示将ps指向函数sum

    函数调用方法:

    常规:sum(a,b)

    指针:(*ps)(a,b)或者ps(a,b)

    示例(仅核心代码):

    函数定义:

    double betsy(int lns)

    {

    return 2*lns;

    }

    double pam(int lns)

    {

    return 2*lns+5*lns*lns;

    }

    void estimate(int lines,double (*pf)(int))

    {

    using namespace std;

    cout<<lines<<"lines will take "<<(*pf)(lines)<<"hours";

    }

    main函数:

    int main()

    {

    using namespace std;

    int code=90;

    estimate(code,betsy);

    estimate(code,pam);

    return 0;

    }

    深入探讨函数指针

    例:声明一个函数指针:

    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};

    可使用typedef进行简化

    typedef可创建类型别名

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

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

    使用:p_fun p1=f1;

  • 相关阅读:
    读《构建之法》阅读与思考
    软工沉浮沉沉沉沉沉沉…记事
    四则运算截图and代码
    2016012000郭慕然+散列函数的应用及其安全性
    结对作业之四则运算网页版
    阅读《构建执法》第四章及第十七章有感
    2016012000小学四则运算练习软件项目报告
    有关读《构建之法》的部分思考与疑问
    遇见·软件
    我的——今日学习内容
  • 原文地址:https://www.cnblogs.com/xyb930826/p/5267017.html
Copyright © 2020-2023  润新知