第七章 函数
2020-02-24 10:38:46 7 举报
AI智能生成
C++ plus
作者其他创作
大纲/内容
函数和结构
结构与数组区别
可以按普通变量一样处理结构
结构是按值传递
缺点:数据大,复制结构增加内存要求,降低系统运行速度
结构名只是结构名称,不是地址。取地址要用&
数据可以被返回
单变量
函数参数传递
函数参数传递
按值传递
适用
结构比较小
格式
(struct )student std;
调用:sum(std);
定义:(struct) student sum( (struct) student std )
调用:sum(std);
定义:(struct) student sum( (struct) student std )
引用
std.name
按地址传递
格式
(struct )student std;
调用:sum(&std);
定义:(struct) student sum( (struct) student *ptd )
调用:sum(&std);
定义:(struct) student sum( (struct) student *ptd )
引用
ptd->name
(*ptd).name
其他
想返回结构指针,可以不用返回,在main()中定义一个结构,传递地址过去,存储
结果。//不利于模块化??
结果。//不利于模块化??
函数和string对象
单变量(对象)
按值传递
代码
#include<iostream>
#include<string>
using namespace std;//指令一定要在这里,不然下面的原型不能用
void show_str(string str1);
int main()
{
string str1="i am a apple";
show_str(str1);
cout<<str1;
return 0;
}
void show_str(string str1)
{
getline(cin,str1);
cout<<str1<<endl;
}
结果:
输入: i am a student
输出: i am a student
i am a apple
地址传递??
代码
#include<iostream>
#include<string>
using namespace std;//指令一定要在这里,不然下面的原型不能用
void show_str(string *);
int main()
{
string str1;
show_str(&str1);
cout<<str1;
return 0;
}
void show_str(string *pstr1)
{
getline(cin,*(pstr1));//要加*才能输入
cout<<*(pstr1)<<endl;//要加*才能输出string的值,不然输出的是地址
}
结果:
输入: i am a student
输出:
i am a student
i am a student
结果:
输入: i am a student
输出:
i am a student
i am a student
注意
名称string位于std中函数原型要
void sum(std::string )
void sum(std::string )
函数和array对象
前言
类对象基于结构,结构编程方面的考虑因素也适用于类对象
可以按值传递,也可以传递指针
格式
按值传递
格式
std::array<double,4> expenses;
调用:show(expenses);
定义:void show(std::array<double,4> da)
调用:show(expenses);
定义:void show(std::array<double,4> da)
说明
array位于名称空间std中
array<double,4>在定义和调用都是这样子,为什么呢??
存在效率不高问题
引用
在函数中,da[1]...
按地址传递
格式
调用:fill(&expenses)
定义:void fill(std::array<double,4> *pa)
定义:void fill(std::array<double,4> *pa)
引用
(*pa)[i]
当成结构,因为pa-> 不可以,所以只有这种?
*(pa+i)可以吗???不可以,不要弄混了
*(pa+i)可以吗???不可以,不要弄混了
其它
const std::array<std::string,Seasons> Snames={"spring","summer","fall","winter"}
递归
前言
c++不允许main() 调用自己
c允许
c允许
原理
void recurs(argument)
{
statements1
if(test)
recurs(arguments)
statement2
}
{
statements1
if(test)
recurs(arguments)
statement2
}
如果if 为ture ,每个recurs()调用按顺序执行statement1,不执行statement2,
直到if 为false,statement2将按相反顺序执行。
直到if 为false,statement2将按相反顺序执行。
每个递归调用都创建自己的一套变量,如果要递归5次,每次传递都是n,
5次调用后,将有5个独立的n,内存地址都不一样
5次调用后,将有5个独立的n,内存地址都不一样
例子
代码
说明
subdivide调用自己两次,一次针对左,一次针对右。//
有点难想出。
有点难想出。
subdivide调用次数呈几何数增长,调用依次导致两次调用,
调用两次导致4词调用,一共调用64次,所以可以填充
64个元素
调用两次导致4词调用,一共调用64次,所以可以填充
64个元素
函数指针
前言
函数有地址,函数地址是存储其机器语言代码的内存开始地址
好处
允许在不同时间传递不同函数的地址,可以在不同的时间使用不同的函数
获取函数地址
函数头是其地址,think() 是函数。
函数作为参数的调用格式
process(think)
说明
区分 process(think())
声明函数指针
double pam(int );
double (*fp)(int);
fp=pam;
/
doubel (*fp)(int)=pam
double (*fp)(int);
fp=pam;
/
doubel (*fp)(int)=pam
说明
返回类型,参数个数,类型都必须与原函数相同
一般不在调用函数中直接声明函数指针,在传递过程中的被调函数定义中声明函数指针(如下)
*fp = pam,注意声明的时候有(),没有的意思是返回double *
格式
原型:void estimate( int lines, double (*fp)(int ));
调用:estimate(50,pam)// pam 原型: double pam(int )
定义:void estimate (int lines,double (*fp)(int))
调用:estimate(50,pam)// pam 原型: double pam(int )
定义:void estimate (int lines,double (*fp)(int))
说明
这样子,说明estinmate 接受函数参数时,没有执行函数,只是接收。
如果要执行函数,在es函数中在用 *fp 来调用
如果要执行函数,在es函数中在用 *fp 来调用
使用指针调用函数
在estimate使用 (*fp)当pam
double x=(*fp)(5)
double x=(*fp)(5)
在C++中,可以直接用fp当作函数名
double x=fp(5);
double x=fp(5);
const 用于函数返回值
2用const修饰函数的返回值
如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。例如函数
const char * GetString(void);
如下语句将出现编译错误:
char * str = GetString();
正确的用法是
const char * str =GetString();
如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const修饰没有任何价值。
例如不要把函数int GetInt(void) 写成const int GetInt(void)。
深入探讨
double * f1( double a[] , int n )
double * f2(double [],int n)
double * f3(double *, int n)
double * f2(double [],int n)
double * f3(double *, int n)
三者都是一样的
单函数指针
声明
父节点
double * (*p1)(double * ,int )
p1=f1;
p1=f1;
初始化
double * (*p1)(double * ,int ) = f1 ;
自动声明
auto p1= f1;
用指针调用函数
(*p1)(av,3)/ p1(av,3)——返回 double 指针
*(*p1)(av,3)/ *p1(av,3 )——返回double 指针指向的值
函数指针数组
声明
父节点
double * (*pa[3])(double *, int n);
pa[0]=f1;
pa[1]=f2;
pa[2]=f3;
pa[0]=f1;
pa[1]=f2;
pa[2]=f3;
初始化
double * (*pa[3])(double *, int n) = { f1, f2, f3};
自动
不可以,自动类型推断只能用于单值初始化
但是可以声明同样类型的数组
auto pb = pa;
auto pb = pa;
调用
double * x=( *pa[0])( av,3)/pa[0](av,3)
double x=*( *pa[0])( av,3)/ *pa[0](av,3)
指向 函数指针数组 的指针
声明
父节点
double * (*(*pc)[3])(double * ,int n) ;
pc=&pa; //double * (*pa[3])(double *, int n) = { f1, f2, f3};
pc=&pa; //double * (*pa[3])(double *, int n) = { f1, f2, f3};
父节点
double * (*(*pc)[3])(double * ,int n) = &pa
子主题
auto pc= &pa;
调用
double * x= (*pd)[i](av,3)/ (*(*pd)[i])(av,3)
double x=*(*pd)[i](av,3) /*(*(*pd)[i])(av,3)
使用typedf简化
自己
double (*p1)(double ,int );
p1=f1;
p1=f1;
简化
=
typedef double (*p_fun)(double , int);
p_fun p1;
p1=f1;
typedef double (*p_fun)(double , int);
p_fun p1;
p1=f1;
不容易犯错,容易理解
自动
auto p1=f1;
容易错
其他
停止整数数组录入的方法
哨兵数字 -1
输入英文使输入格式错误
输入n 个数组的数
先设置足够大的数N ,连续录入,若条件不符合则跳出。
停止字符串录入的方法
一行一行录入
cin>>str
cin.getline(str,Stsize)
string list[i]
getline(cin,list[i])
getline(cin,list[i])
cin.get(str,Stsize)
一个一个录入
while((ch=cin.get()) != '\n')
while( cin.get(ch) )
ctrl + z enter才结束。
int ch;
while( (ch=cin.get()) != EOF; )
while( (ch=cin.get()) != EOF; )
哨兵字符作为结束
基本知识
使用函数步骤
原型
方式
头文件
一般是系统的函数用头文件。当然,也可以写。
写原型
原型语法
原型中可以有变量名,也可以没有,变量名相当于占位符
有时提供变量名,可以助于容易理解 。特别是两个类型一样时,当然也要变量名有意义。
需要原型原因
原型描述了函数到编译器的接口,将函数返回值的类型以及参数的类型和数量告诉编译器
原型功能
编译器正确处理函数返回值
编译器检查使用的参数数目是否正确
检查参数类型是否正确。不正确转换成正确的类型
编译阶段进行原型化——静态类型检查。可以捕获许多在运行阶段难以捕获的错误
其它
C++中,不指定参数列表时,使用... ,表示将在后面定义参数列表。
void say_bye(...);
void say_bye(...);
定义
有返回值
返回值结果的类型应该是 typename类型或者可以被转换为typename 类型
返回值的类型 ,不能是数组,可以是其他任何类型——整数、浮点数、指针、结构、对象
数组可以作为结构或对象返回
数组可以作为结构或对象返回
无返回值
函数返回原理
书p204
调用
函数参数和
按值传递
按值传递
按值传递
使用副本,不影响原来的数据
参数
类型
实参
参数
形参
参量
形参和局部变量区别
形参从调用 的函数获取自己的值,局变从函数中获取自己的值
其它
cin>>number>>picks
(10*9)/(2*1) 和 (10/2)*9/1
当数很大时,前者的中间值(90)比后者大,可能会超出最大浮点数
函数和数组
原型:int sum(int [],int );
调用:sum(a,n);
i定义:int sum(int array[],int n)
调用:sum(a,n);
i定义:int sum(int array[],int n)
说明
定义的时候,[]可以不带任何值,表明可以将任何长度的数组传递给该函数
这个时候尽量要有n??
array虽然看上去是数组,实际上是指针。可以将array看作是数组那样使用
int array[] 替代了int *array,当用于函数头或函数原型时,两者是相同的
也就是可以进行array++运算 ??是的。
sizeof array 也是存储地址字节的长度,不是数组长度,这也是必须显式传递
数组长度的原因,指针没有指出数组的长度
数组长度的原因,指针没有指出数组的长度
array[i] == *(array + i)
&array[i] == array + i
&array[i] == array + i
可以改变数组的起始位置
sum(a+4,n);/sum(&a[4],n)
int sum(int array[], int n)
int sum(int array[], int n)
为将数组类型和长度告诉数组处理函数,最好通过两个不同的参数传递它们:
void sum(int arr[],int n)
void sum(int arr[5])//虽然可以执行但是不太好
void sum(int arr[5])//虽然可以执行但是不太好
数组参数的
传递方式
传递方式
地址传递,不是值传递
变量——使用该变量的副本
数组——使用原来的数组
数组——使用原来的数组
好处
节省复制整个数组所需要的时间
坏处
破坏原来的数据
解决办法 const
const保护数组
格式
void sum(const double ar[], int n)
原理
表明指针ar指向的是常量数据,不能使用ar 修改数据
*(ar)=0;//invaild
ar++ //vaild
ar++ //vaild
函数处理数组
(元素区间法)
(元素区间法)
方法
传统:数组的数据类型,起始位置,元素数量 给函数
制定元素区间,传递两个指针,一个指向开头,一个指向指针标识数组的尾部。
说明
标识数组尾部的参数是指向最后一个元素后面的指针
格式
原型:int sum(int * begin,int * end);
调用:sum(a,a+Arsize);
定义:int sum(int * begin,int * end);
调用:sum(a,a+Arsize);
定义:int sum(int * begin,int * end);
元素引用
数组表示法
一般不用。且指针法也是一般不改变 begin ,end 的指向,而是再声明一个指针来改变
指针法
for(pt=begin ; pt != end ; pt++)
为什么不直接
for(; begin !=end; begin ++)//??
for(; begin !=end; begin ++)//??
应用
指向不通区间
sum(a+1,a+3);
指针和const
指向的值不能改变
含义
指针指向一个常量对象
格式
int age=39;
const int *pt = &age;
const int *pt = &age;
*pt += 1//invalid
age +=1 //valid.
可以直接通过age 变量修改age,不可以间接通过 *pt 来改变age
age +=1 //valid.
可以直接通过age 变量修改age,不可以间接通过 *pt 来改变age
const变量和指针
对应关系
对应关系
const变量
const指针
const指针
const int age = 39;
const int *pt =&age; //valid
const int *pt =&age; //valid
*pt+=1 ;
age+=1; //invalid
age+=1; //invalid
const变量
常规指针
常规指针
const int age = 30;
int *pt =&age //invalid
int *pt =&age //invalid
意味着可以通过 *pt 修改age,这样会造成混乱,编译器不允许这样的情况
非要这样做,可以通过强制类型转换突破现象//??怎么样?
指向指针的指针情况情况:
const int **pp2;
int *p1;
pp2=&p1 //invalid
int *p1;
pp2=&p1 //invalid
原因
const int **pp2;
int *p1;
const int n=13;
pp2=&p1;
*pp2=&n;
*p1=10;//造成混乱
int *p1;
const int n=13;
pp2=&p1;
*pp2=&n;
*p1=10;//造成混乱
const int **pp2;
const int *p1;
pp2=&p1;//valid;
const int *p1;
pp2=&p1;//valid;
总结
如果数据类型不是指针,
非const数据,const 数据地址——const指针
非const数据地址 ——非const指针
非const数据,const 数据地址——const指针
非const数据地址 ——非const指针
其他
const int a[5]={1,2,3,4,5};
不能将数组名a,传递给非const 类型的指针
不能将数组名a,传递给非const 类型的指针
sum(a,n);
void sum( int arr[],int n)//invalid
void sum( int arr[],int n)//invalid
const int *pt= a; 不意味着pt指向的位置不可以改变,即pt ++;pt+=1,是可以的.
pt 不指向a,指向另一个值也是可以的。
pt 不指向a,指向另一个值也是可以的。
指向的位置不能改变
含义
指针本身声明为常量
格式
int age = 39;
int * const pt = &age
const int * pt =&age//对比
int * const pt = &age
const int * pt =&age//对比
说明: 上 :pt 是const ,但*pt 不是const,*pt可以修改
下:pt不是const,*pt 是const;
下:pt不是const,*pt 是const;
关系
const int age = 39;
int * const pt = &age;
int * const pt = &age;
*pt, pt 都不可以修改。
//这样也可以
//这样也可以
函数和二维数组
前言
int data[3][4] = {...}
data ——数组名,该数组有3个元素。每个元素是指向由4个int组成的数组。
data ——数组名,该数组有3个元素。每个元素是指向由4个int组成的数组。
感觉没什么区别:
data +1 的区别。
data +1 的区别。
a[][];
&a=0x6bfee0
&a=0x6bfee0
a=0x6bfee0
&a[0]=0x6bfee0
a[0]=0x6bfee0
&a[0][0]=0x6bfee0
//含义不一样。不能理解
数组的名字代表了此数组的首地址,对数组再取地址,它的值仍是相同的。
int a[5] = {1,2,3,4,5}; a ,&a 它们的值是相同的
对数组取地址,类型可以当做指向此种数组的指针来使用,这里没有确定说它是指针
int a[5] = {1,2,3,4,5};
&a 类型当做为指向一维数组(5个元素)的指针,即 int (*)[5]
于是 &a+1 的数据为a的地址 + 5*sizeof(int)
int b[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
&b 类型当做为指向二维数组(3*4个元素)的指针,即 int (*)[3][4]
&b+1 的数据为b的地址 + 3*4*sizeof(int)
这里没有说 &数组 就是指针,因为再 &(&数组名) 或 &(&(&数组名) ) ,它们与 数组名的值都是一样,显然指针是没有这种特性的
指向数组的指针的解引用
三维数组,可以看做是指向二维数组的指针,解引用后为指向一维数组的指针,即二维数组
二维数组,可以看做是指向一维数组的指针,解引用后为指向类型(如int)的指针,即一维数组
一维数组,可以看做是指向类型(如int)的指针,解引用后为类型数(如int数),即一个数
int main(int argc, char* argv[])
{
int a[5] = {1,2,3,4,5};
int b[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
printf("%p/n",&a); /* a取地址(类型当做指向一维数组(有5个元素)的指针 ,即int (*)[5] ) ,对数组取地址,内容与数组的首地址相同 ,记为 pA */
printf("%p/n",&a+1); /* pA + 5*sizeof(int) ,&a指向一维数组,加1则跳过5个元素*/
printf("/n");
printf("%p/n",a); /* a的值(数组a的首地址, 类型当做是 int *) 以地址形式打印 值仍为 pA*/
printf("%p/n",a+1); /* pA + 1*sizeof(int) , a指向int , 加1跳过1个元素 */
printf("/n");
printf("%p/n",&b); /* b取地址(类型当做指向二维数组(有3*4个元素)的指针 ,即int (*)[3][4] ) ,对数组取地址,内容与数组的首地址相同 ,记为 pB */
printf("%p/n",&b+1); /* pB + 3*4*sizeof(int) ,&b指向二维数组,加1则跳过3*4个元素*/
printf("/n");
printf("%p/n",b); /* b的值 (数组b的首地址, 类型看做是 int (*)[4] ) 以地址形式打印 值仍为 pB*/
printf("%p/n",b+1); /* pB + 4*sizeof(int), b指向一维数组(4个元素) ,加1跳过4个元素 */
printf("/n");
printf("%p/n",*b); /* 二维数组解引用为一维数组 *b == *(b+0) == b[0] */
printf("%p/n",*b+1); /* 一维数组加1, 跳过1个元素 ,即 加上 1*sizeof(int) */
printf("/n");
printf("%p/n",**b); /* 一维数组解引用为一个类型数 **b == *(*(b+0) +0) == *(b[0] +0) == b[0][0] */
printf("%p/n",**b+1); /* b[0][0]+1 == 2 */
getch();
return 0;
}
{
int a[5] = {1,2,3,4,5};
int b[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
printf("%p/n",&a); /* a取地址(类型当做指向一维数组(有5个元素)的指针 ,即int (*)[5] ) ,对数组取地址,内容与数组的首地址相同 ,记为 pA */
printf("%p/n",&a+1); /* pA + 5*sizeof(int) ,&a指向一维数组,加1则跳过5个元素*/
printf("/n");
printf("%p/n",a); /* a的值(数组a的首地址, 类型当做是 int *) 以地址形式打印 值仍为 pA*/
printf("%p/n",a+1); /* pA + 1*sizeof(int) , a指向int , 加1跳过1个元素 */
printf("/n");
printf("%p/n",&b); /* b取地址(类型当做指向二维数组(有3*4个元素)的指针 ,即int (*)[3][4] ) ,对数组取地址,内容与数组的首地址相同 ,记为 pB */
printf("%p/n",&b+1); /* pB + 3*4*sizeof(int) ,&b指向二维数组,加1则跳过3*4个元素*/
printf("/n");
printf("%p/n",b); /* b的值 (数组b的首地址, 类型看做是 int (*)[4] ) 以地址形式打印 值仍为 pB*/
printf("%p/n",b+1); /* pB + 4*sizeof(int), b指向一维数组(4个元素) ,加1跳过4个元素 */
printf("/n");
printf("%p/n",*b); /* 二维数组解引用为一维数组 *b == *(b+0) == b[0] */
printf("%p/n",*b+1); /* 一维数组加1, 跳过1个元素 ,即 加上 1*sizeof(int) */
printf("/n");
printf("%p/n",**b); /* 一维数组解引用为一个类型数 **b == *(*(b+0) +0) == *(b[0] +0) == b[0][0] */
printf("%p/n",**b+1); /* b[0][0]+1 == 2 */
getch();
return 0;
}
int data[3]
data ——数组名,指向第一个元素。
data ——数组名,指向第一个元素。
格式
data[3][4]
原型:void sum(int [][],int );
调用:sum(data,3);
定义:void sum(int (*ar2)[4] ,int size )
void sum(int ar2[][4],int size)
原型:void sum(int [][],int );
调用:sum(data,3);
定义:void sum(int (*ar2)[4] ,int size )
void sum(int ar2[][4],int size)
说明
(*ar2)[4]表示一个指向 由4个元素组成的数组 的指针,ar2是指向指针的指针
指针类型 指定了列数,sum()函数只接受由4列组成的数组。长度变量指定了行数,
所以sum()对数组的行数没有限制
所以sum()对数组的行数没有限制
引用
数组法
直接把ar2看作是二维数组
ar2[i][j]
指针法
*(*( ar2 + i ) +j )
说明
ar2
ar2+r
*(ar2 + r)
*(ar2 +r ) + c
其他
在生命参数ar2时,没有使用const ,const技术只能用于指向基本类型的指针。
ar2是指向指针的指针
ar2是指向指针的指针
函数和c-风格字符串
函数调用
参数表示
字符串表示方式
char数组
char good[15]={};
strlen(good)
strlen(good)
“”括起的常量
strlen("i am a ")
char字符串指针
char *ptr=“i am a”;
strlen(ptr);
strlen(ptr);
表示方式都是char*类型,所以都可以作为函数参数
长度
不需要长度作为参数传递给函数
可用\n看结尾
定义
int sum(char *str);
int sum(char str[])
函数中的元素引用
数组表示法
就算 int sum(char *str);也可以
指针法
返回c-风格字符串的函数
代码
p227
说明
一定要使用new来创建。
char * buildstr(char c,int n)
{
char *pstr ="i am a student";
return pstr;
}//这样是错误的
{
char *pstr ="i am a student";
return pstr;
}//这样是错误的
char * buildstr(char c,int n)
{
char *pstr =new char[n];
pstr="i am a student";
return pstr;
}
{
char *pstr =new char[n];
pstr="i am a student";
return pstr;
}
其它
用const防止被修改
自由主题
0 条评论
下一页