第7讲——函数初步

咱们知道,C++自带了一个包含函数的大型库(标准ANSI库加上多个C++类),但这并不能知足咱们的需求,咱们须要编写本身的函数。但咱们在编写函数时为了提升编程效率,可更深刻地学习STL和BOOST C++提供的功能。程序员

 

咱们先学习一下库函数,它是已经定义和编译好的函数,同时可使用标准库头文件提供其原型,所以只需正确地调用这种函数便可。例如,标准C库中有一个strlen()函数,相关的标准头文件cstring包含了strlen()和其余一些与字符串相关的函数的原型。这些预备工做使程序员可以在程序中随意使用strlen函数。编程

 

因此,建立本身的函数时,须要咱们自行处理这3个方面——定义、提供原型和调用。数组

 

咱们知道函数分为:有返回值的函数和没有返回值的函数。cookie

对于有返回值的函数,C++对于返回值的类型有必定的限制:不能是数组,但能够是其余任何类型——整数、浮点数、指针、结构和对象。app

有趣的是,虽然不能直接返回数组,但能够将数组做为结构或对象组成部分来返回。函数

下面咱们来讨论函数是如何处理数组的。学习

若要设计一个对数组中元素求和的函数,那么这个函数须要知道对哪一个数组进行累计,所以咱们须要将数组名做为参数传递给它。为使函数通用,而不限于特定长度的数组,还须要传递数组长度。下面咱们来看一看函数头及其其余部分:spa

int sum_arr(int arr[],int n)   //arr是数组名,n是数组长度

这看上去表示:方括号指出arr是一个数组,而方括号为空则代表,能够将任何长度的数组传递给该函数。设计

但实际状况并不是如此:arr实际上并非数组,而是一个指针!好消息是,在编写函数的其他部分时,能够将arr看做是数组。指针

下面咱们来研究研究函数如何使用指针来处理数组:

咱们知道,C++将数组名视为指针。以前咱们学过C++将数组名解释为第一个元素的地址:cookies == &cookies[0] 。可是咱们应该要知道的是:①数组声明使用数组名来标记存储位置;②对数组名使用sizeof将获得整个数组的长度(以字节为单位);③将地址运算符&用于数组名时,将返回整个数组的地址。

当咱们在调用函数中如此调用数组时:int sum = sum_arr(cookies, ArSize); (cookies是数组名),根据C++规则,cookies是其第一个元素的地址,所以函数传递的是地址。而因为数组的元素的类型为int,所以cookies的类型必须是int指针,即int*。这代表,正确的函数头应该是这样的:

int sum_arr(int *arr,int n)    //arr是数组名

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

 

有一点须要记住,不一样于传递常规变量(须要新建变量存储值),传递数组时,函数将使用原来的数组,这将能够节省复制整个数组所需的时间和内存。

 

下面,咱们来讨论函数与结构。

相比于数组,涉及到函数时,结构变量的行为更接近于基本的单值变量。也就是说,与数组不一样,结构将其数据组合成单个实体或数据对象,该实体被视为一个总体。

那么,咱们给出结构在函数中的特性:①能够按值传递结构,就像普通变量那样,同时,函数将使用结构的副本;②函数能够返回结构,与数组名就是第一个元素的地址不一样的是,结构名只是结构的名称,要得到结构的地址必须使用地址运算符&。

使用结构编程时,最直接的方式是像处理基本类型那样处理结构。也就是说,将结构做为参数传递,并在须要时将结构用做返回值使用。因为按值传递结构可能因结构过大而耗费大量内存,因此许多C程序员倾向于传递结构的地址,而后使用指针来访问结构的内容,然而C++提供了第三种选择——按引用传递。

【按值传递】

struct travel_time{
	int hours, mins;
};

travel_time sum(travel_time t1, travel_time t2)	//返回类型为结构类型 
{
	...
}

int main()
{
	travel_time day1 = {5, 45};
	travel_time day2 = {4, 55};
	travel_time trip = sum(day1, day2);		//结构变量做为参数 
}

在这里,结构体类型travel_time就像是一个标准的类型名,可被用来声明变量、函数的返回类型和函数的参数类型。

【按地址传递】

与传值操做不一样的是,这能让函数对原始结构进行操做,而不是结构副本。

传递结构的地址而不是整个结构以节省时间和空间,此时须要使用指向结构的指针。

须要注意的是,若是咱们但愿在函数中不修改结构,咱们应使用const修饰符。

 

关于函数和string对象,待往后学完string再来填坑。(p235)

 

忽然看到书上有函数指针,我靠,别逗我笑,这种低级书应该只是形式上地描写一下。咱们也先大概谈谈吧。

看了一下,书上讲的篇幅还挺多,我如今没时间扯这个了,之后须要再看。(p241)

 

温故而知新

【使用函数的3个步骤】

定义函数;提供原型;调用函数。

【编写一个接受3个参数的函数:指向数组区间中第一个元素的指针、指向数组区间最后一个元素后面的指针,以及一个int值,并将数组中每个元素都设置为该int值】

void set_array(int *begin, int *end, int value)
{
	while(begin != end){
		*begin = value;
		begin++;
	}
} 

//注:在函数内部移动指针,离开函数后,指针恢复到初始位置(应该是在函数内部建立了一个指针副本)。所以无需在函数内部新建立一个指针。

【为何不对类型为基本类型的函数参数使用const限定符】

答:由于函数在调用参数时,使用的是一个副本,而不是原来的数,所以不会修改做为实参的基本类型的值。而指针不一样,指针为函数参数时,能够经过修改直着,来修改指针所指向的值。

【C++程序可使用哪3种C-风格字符串格式】

答:字符串能够被储存在char数组中,可使用带双引号的字符串来表示(好比"abc",但这种没法被修改),也能够用指向字符串第一个字符的指针来表示。

【表达式*"pizza"的含义是什么?"taco"[2]呢】

答:

*"pizza"的含义是:"pizza"是一个常量字符串,其名字表示为指向其地址的指针(类型为char*),对这个指针解除运算,是字符串的第一个字符——即p。*"pizze"的结果是:p

"taco"[2]的含义是:原理同上,这个常量字符串的第三个字符——是c。

以上答案存疑。

参考答案给的是:C++将字符串解释为指其第一个元素的地址,即p和t的地址,*给出第一个元素的值,[2]给第三个元素的值,因此分别是p和c。

【C++容许按值传递结构,也容许传递结构的地址。若是glitz是一个结构变量,如何按值传递他它?如何传递他的地址?这两种方法有何利弊?】

答:

按值传递则是传递他的类型,而后glitz做为参数进行传递。按地址传递则是参数使用结构指针。

按值传递的好处是不会修改原结构变量,按地址传递的好处正好是能够在函数内修改原结构变量。

假如结构类型是abc,则声明结构是abc glitz;

按值传递函数原型假如为:void mmm(abc);

按地址传递函数原型假如为:void mmm(abc*);

glitz做为参数时,按值是glitz,按地址则为&glitz。

补充:按值传递将自动保护原始数据,但这是以时间和内存为代价的(由于要复制副本),按地址传递可节省内存和时间,但不能保护原始数据,解决办法是使用const限定符。

【假设有以下结构声明:struct applicant  {char name[30];  int credit_ratings[3];};

a。编写一个函数,它将applicant结构做为参数,并显示该结构的内容。

b。编写一个函数,他将applicant结构的地址做为参数,并显示该参数指向的结构的内容。】

void show_1(applicant m)
{
	cout<<m.name<<endl;
	for(int i=0;i<3;i++)
		cout<<m.credit_ratings[i]<<endl;
}

void show_2(applicant *m)
{
	cout<<m->name<<endl;
	for(int i=0;i<3;i++)
		cout<<(*m).credit_ratings[i]<<endl;
}
相关文章
相关标签/搜索