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次