C++ Primer Plus学习笔记(五)函数基本知识

1. 基本用法

(1)提供函数定义
(2)提供函数原型
(3)调用函数

void test(int ,int);//函数原型,参数名字可省略
void test(int a ,int b)
{
    statement;
    return;
}
test(a,b);//函数调用

注:函数返回值可以是除数组外的任意类型,比如对象,结构,基本数据类型等

2. 函数与数组

2.1 基本用法

函数用于传递数组参数时,本质是是传递指针,而不是数组内容(所以数组长度未知,需要手动传入)

int sum_arr(int arr[],int n)
{

}

注:只有用于函数头和函数声明时,int *arr 和 int arr[] 是完全等同的,因此上述代码等同于:

int sum_arr(int* arr,int n)
{
}

假如arr被声明为指针,ar被声明为数组:

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

因此,不仅指针存在变量+n表示后移n个变量地址,数组名也存在同样的操作

例子:

int num[5] = {1,2,3,4,5};
int sum_arr(int a[], int n);
int sumFirstTwo = sum_arr(num, 2);//传递值为num,相当于num+0
int sumLastThree = sum_arr(num + 2, 3);//传递值为num+2,相当于首地址后移两个,指向第三个值
int sum_arr(int a[], int n)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
        sum += a[i];
    return sum;
}

2.2 一种错误输入类型纠正方法

double factor;
while(!(cin>>factor))//输入类型错误
{
    cin.clear();//首先清除错误标记,使得后续可以接受输入
    while(cin.get() != '\n')
        continue;//直到输入错误的行尾结束,才可以继续输入
    cout << "请输入正确的值:" <<endl;
}

2.3 使用数组区间的函数

类似于STL库,传递两个指针,一个标识开头,一个标识结尾。注意,标识结尾的指向数组最后一个元素后面的指针,而不是指向最后一个元素。

const int Size = 5;
int num[Size] = {1,2,3,4,5};
int sum_arr(const int *begin, const int * end)//const 用于修饰,使得指针不能被修改
{
    const int * pt;
    int sum = 0;
    for(pt = begin; pt != end; pt++)
        sum += *pt;
    return sum;
}
int sum = sum_arr(num,num+Size);//num+Size,指向数组num后面的指针

2.4 函数与二维数组

二维数组相当于行数的数组,比如int a[2][3],相当于3个数组,每个int数组个数为4(列数),因此用于函数传递时,列数为固定,行数作为参数
函数原型:

int a(int (*arr)[4],int size);

其中,二维数组为int (*arr)[4]。
另一种可读性更强的表达(与上面相同):

int a(int arr[][4],int size);

上面的都表明arr为指针,而不是数组。
注:arr表示数组名,arr二维数组指针需要解除两次引用才可以得到值:

arr[r][c] == *(*(arr+r)+c) 

3. 函数与C-风格字符串

3.1 字符串方式

(1)char数组
(2)用引号引起的字符串常量
(3)被设置为字符串的地址的char指针
形参为char * 类型

char name[13] = "asdsad";
int c_in_car(const char *str);
c_in_car(name);

C-风格字符串和常规字符串区别在于是否有内置的结束字符。

3.2 函数返回

虽然说函数不返回字符串,但是可以返回字符串的地址。
示例如下:

char *buildstr(char c,int n);//创建n个c的字符串
char c = 'a';
int n = 10;
char *buildstr(char c, int n)
{
    char *pstr = new char[n+1];//创建n个字符的字符串,需要n+1个空间
    pstr[n] = '\0';
    while(n-->0)
        pstr[n] = c;
    return pstr;
}
char *ps = buildstr(c,n);
delete [] ps;//虽说在函数内部创建的动态结构被销毁,但是在main里面仍可以申请空间,
//并且必须用delete删除动态结构

4. 函数和结构

4.1 常规用法

普通常规用法,结构和普通变量一样,可以直接传递,但是普通结构形参和实参关系,形参的改变不会引起实参的变化。

4.2 按地址传递

此时,形参利用(* ptr指针),实参利用(&结构名),可以实现结构的改变。

5. 函数和string对象

5.1 常规用法

普通常规用法,string对象和普通变量一样,可以直接传递,但是普通结构形参和实参关系,形参的改变不会引起实参的变化。

void testPrintString(string  a)
{
    cout << a;
}
string b = "123";
testPrintString(b);//简单传递
//输出结果123

5.2 按地址传递

此时,形参利用(* ptr指针),实参利用(&结构名),可以实现结构的改变。

void testPringString(string  *a)//形参用*
{
    (*a)[2] = 'a';//一定要用()将*包起来

}
string b = "12345";
testPringString(&b);//实参用&
//输出结果,b:12a45

6. 函数与array对象

array 与结构、string类似。

7. 函数指针

7.1 基本声明

函数指针意思是指向函数的指针,其声明类似于普通函数声明,区别相当于(*p)替换函数名

double pam(int);
double (*pf)(int);
pf = pam;

注:函数声明和函数指针声明要求变量列表对应相同,并且返回类型相同

7.2 函数指针调用函数

方式一:

相当于(*pf)相当于函数名,进行调用。承接上面函数声明,其调用形式:

double y = (*pf)(5);

方式二:

在C++中,也允许直接利用指针名进行调用

double y = pf(5);

7.3 函数指针数组和自动类型判断

首先,介绍三个等同表达

const double * f1(double ar[]);
const double * f2(double []);
const double * f3(double *);

函数指针声明:

const double *(*p1)(double ar[]) = f1;

C++11允许自动类型判断,如同上面f1声明,f2声明如下:

auto p2 = f2;//因为f2被声明为函数,自动类型判断p2被转换为f2对应的函数指针

注:自动类型判断只能用于单值,不允许用于初始化列表,详情如下
声明函数指针数组,需要在函数指针名称后面加[3],如下所示:

const double *(*pa[3])(double ar[]) = {f1,f2,f3};
//自动类型判断只能用于单值,不允许用于初始化列表

声明包含三个函数指针的函数指针数组pa后,声明同种类型的可以用自动类型推断

auto pb = pa;

函数指针数组调用如下:

const double * px = pa[0](av);

8. 指针和const

8.1 两种用法

将指针指向的值指定为const,此时指针指向的值不能变,但是指针可以指向新的值;
将指针指定为const,此时指针不能指向新的值,但是指针指向的值可以改变

int a = 10;
const int *ps = &a;//ps为指向const int的指针,可以改变ps指向,但是a不能通过ps改变
int * const ps = &a;//ps为const指针,不能改变指向,但是a可以改变 

8.2 原则

在函数声明中,可以用const声明,使得变量不能被改变,如果改变编译器会报错。
例如:

void test(const int a[],int b)//不允许对数组a改变
{
    statement;
}

原则:可以将常规变量地址赋给const指针,也可以将const变量地址赋给const指针,但是不能将const变量地址赋给非const指针

int age= 2;
const int *pt = &age;//允许,因为pt为指向const in变量的指针

const int age1 = 2;
int *pt1 = &age1;//不允许

9. 利用typedef进行简化

使用说明:

typedef data_type new_name

data_type:已经存在的数据类型或者用户用struct/union等自己声明的类型,new_name是其新名字

9.1 typedef与指针

typedef int * iptr;
#define iptr1 int *
iptr a,b,c;//等同于 int *a, *b, *c
iptr1 a,b,c;//等同于int *a,b,c

注:typedef与define有区别,#define new_name old_name。

9.2 typedef与数组

typedef int iarr[10];
iarr a,b[5];//等同于 int a[10],b[5][10]

9.3 typedef与函数指针

typedef 返回类型 (*Name)(参数列表)
//例如:
typedef int (*Pname)(int n);
int test(int n)
{

}
Pname p1 = test;

10. 函数递归

void recurs(argumentlist)
{
    statement1
    if(test1)
        recurs(arguments)
    statemeng2
}

注:假如recurs函数被递归调用5次,则第一个statement1按函数调用顺序5次,然后statement2部分按与之相反顺序调用5次

猜你喜欢

转载自blog.csdn.net/yanrong1095/article/details/80572498