【C++】第7章 函数 知识总结

来源:互联网 发布:洛克希德马丁公司知乎 编辑:程序博客网 时间:2024/06/01 09:14

《C++ Primer Plus》第7章知识点总结

简单的知识不再进行赘述


形参:函数头中用于接受传递值的变量

实参:传递给函数的值


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

(1)提供函数定义

(2)提供函数原型

(3)调用函数


函数原型

当函数没有写在main函数前面,而是写在后面时,需要在main前面加上函数的原型,例如下面这个例子

#include <iostream>void cheers(int);//这就是原型double cube(double x);//这就是原型int main(){    using namespace std;    cheers(5);    cube(2);    return 0;}void cheers(int n){    using namespace std;    cout<<n<<endl;}double cube(double x){    return x*x*x;}
从这个例子可以看到,原型有两种写法,一种不提供变量名,只提供了参数类型,如void cheers(int);另一种提供变量名,如double cube(double x);

原型确保以下几点:

(1)编译器正确处理函数返回值

(2)编译器检查使用的参数数目是否正确

(3)编译器检查使用的参数类型是否正确


函数和数组

#include <iostream>const int ArSize=8;int sum_arr(int arr[],int n);int main(){    using namespace std;    int cookies[8]={1,2,4,8,16,32,64,128};    int sum=sum_arr(cookies,ArSize);    cout<<sum<<endl;    return 0;}int sum_arr(int arr[],int n){    int total=0;    for(int i=0;i<n;i++)        total=total+arr[i];    return total;}
这是一个最简单的例子


函数如何使用指针来处理数组

首先,在大多数情况下,C++和C语言一样,也将数组名视为指针。C++将数组名解释为其第一个元素的地址

cookies==&cookies[0]

当然,该规则有一些例外:

(1)数组声明使用数组名来标记存储位置

(2)对数组名使用sizeof将得到整个数组的长度(以字节为单位),比如sizeof cookies的值为32,而sizeof arr的值为4。因为sizeof arr只是指针变量的长度

(3)将地址运算符&用于数组名时,将返回整个数组的地址

在上面的例子中,执行了下面的函数调用

int sum_arr(cookies,ArSize);

其中cookies是数组名,而根据c++规则,cookies是其第一个元素的地址,因此函数传递的是地址。由于数组的元素的类型为int,因此cookies的类型必须是int指针,即int*。这表明,正确的函数头应该是:

int sum_arr(int * arr,int n)

其中,int * arr替换了int arr[]。这证明两个函数头都是正确的,因为在C++中,当且仅当用于函数头或函数原型中,int *arr和int arr[]的含义才是相同的。它们都意味着arr是一个int指针。然而,数组表示法(int arr[])提醒用户,arr不仅指向int,还指向int数组的第一个int。

就目前而言,要牢记以下两个恒等式:

arr[i]==*(ar+i)

&arr[i]==ar+i

将指针(包括数组名)加1,实际上是加上了一个与指针指向的类型的长度(以字节为单位)相等的值。对于便利数组而言,使用指针加法和数组下标时等效的。

为将数组类型和元素数量告诉数组处理函数,请通过两个不同的参数来传递他们

void fillArray(int arr[],int size);

而不要试图使用方括号表示法来传递数组长度

void fillArray(int arr[size]);——这是错误的


指针和const

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

void show_array(const double ar[],int n);

该声明表明,指针ar指向的是常量数据。这意味着不能使用ar修改该数据,也就是说可以使用像ar[0]这样的值,但不能修改


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

第一种方法是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值

第二种方法时将只做了本身声明为常量,这样可以放置改变指针指向的位置


下面来看几个例子

    int age=39;    const int *pt=&age;    //该声明指出,pt指向一个const int(这里为39),因此不能使用pt来修改这个值        *pt+=1;    cin>>*pt;//这两语句都不合法,因为不能使用pt来修改它所指向的值        *pt=20;    age=20;    //pt指向age,pt是const的,而age不是,因此不可以通过pt修改age的值但是可以直接通过age变量来修改age的值        const float g_earth=9.80;    const float *pe=&g_earth;    //这种情况下,既不能使用g_earth来修改值9.80,也不能使用pe来修改        const float g_moon=1.63;    float *pm=&g_moon;    //这种情况在c++中是非法的,因为如果将g_moon的地址赋给pm,    //则可以使用pm来修改g_moon,这使得g_moon的const状态变得无效        int age=39;    const int *pt=&age;    //第二个声明中const只能防止修改pt指向的值(这里为39),而不能防止修改pt的值。    //也就是说可以将一个新地址赋给pt,如下    int sage=80;    pt=&sage;    //此时pt指向地址的值为80,但仍然不能使用pt来修改指针的值        int sloth=3;    const int *ps=&sloth;    int *const finger=&sloth;    //这里仔细观察第二和第三个声明中关键字const位置的不同    //第二个声明中,不允许使用ps来修改sloth的值,但允许将ps指向另一个位置    //第三个声明中,finger只能指向sloth,但允许使用finger来修改sloth的值    //简而言之,finger和*ps都是const,而*finger和ps不是

函数和二维数组

假设存在下面的代码

int data[3][4]={{1,2,3,4},{5,6,7,8},{2,4,6,8}};

int total=sum(data,3);

那么sum()的原型是:int sum(int (*ar2)[4],int size);

因为data是一个数组名,该数组有3个元素。第一个元素本身是一个数组,有4个int值组成。因此data的类型是指向由4个int组成的数组的指针。下面这是另一种格式,含义一样,但可读性更强

int sum(int ar2[][4],int size);

由于指针类型指定了列数,因此sum()函数只能接受由4列组成的数组。但长度变量指定了行数,因此sum()对数组的函数没有限制


函数和C风格字符串

表示字符串的三种方式:

(1)char数组

(2)用引号括起来的字符串常量(也称字符串字面值)

(3)被设置为字符串的地址的char指针

将字符串作为参数来传递,实际传递的是字符串第一个字符的地址。这意味着字符串函数原型应将其表示字符串的形参声明为char*类型。

C风格字符串与常规char数组之间的一个重要区别是,字符串有内置的结束字符(包含字符,但不以空值字符结尾的char数组只是数组,不是字符串)

下面这里个例子演示如何返回C风格字符串的函数

#include <iostream>const int ArSize=8;char *buildstr(char c,int n);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);    cout<<ps<<"-DONE-"<<ps<<endl;    delete [] ps;//free memory    return 0;}char *buildstr(char c,int n){    //要创建包含n个字符的字符串,需要能存储n+1个字符的空间,以便能存储空值字符    char *pstr=new char[n+1];    pstr[n]='\0';    while(n-->0)        pstr[n]=c;    return pstr;}

函数和结构

传递结构的地址而不是整个结构可以节省时间和空间。传递结构的地址时,由于形参是指针而不是结构,因此应间接成员运算符(->)而不是成员运算符(句点)。另外,做修改时不用返回一个结构体,可以直接对地址上的数据进行修改


函数指针

与数据项相似,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址


获取函数的地址

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


声明函数指针

声明指向某种数据类型的指针时,必须指定指针指向的类型。同样,声明指向函数的指针时,也必须指定指针指向的函数类型。这意味着声明应指定函数的返回类型以及函数的特征标(参数列表)。也就是说,声明应像函数原型那样指出有关函数的信息。例如,一个估算时间的函数原型如下:

double pam(int);

则正确的指针类型声明如下:

double (*pf)(int);

这与pam()声明类似,这是将pam替换为了(*pf)。由于pam是函数,因此(*pf)也是函数,pf就是函数指针


使用指针来调用函数

只需要把(*pf)看作函数名即可