C++ Primer第7章相关问题回顾与总结

1.C++为什么要声明函数,不声明可以吗?

1.首先C++声明函数是为了广大程序员着想,因为也许函数并不都是在一个文件中使用的,如果多个文件同时需要一个函数,那在每个文件中都对同一个函数进行定义效率极低,并且当需要修改维护函数定义时,我的天,意味着你需要一个一个地修改相关函数,那将会是一个庞大的工作量,所以一般将函数声明写在头文件中,文件只需要包含这个头文件就可以使用相关函数。将函数定义写在.cpp工程文件中便于修改和维护。
2.有时候,自己写的函数需要给别人用,比如商业用途。这样的话,不能将整个函数的实现都给对方,这就需要生成一个库文件,而将函数声明放在头文件中来给对方使用。
3.提前声明函数可以很明显地看到多态——即函数的重载,不需要进入函数的定义就能知道函数的形参。并且还有一个好处就是,函数声明可以设定默认形参。总之就是程序员可以一眼明白这个函数大概是干嘛的,而不需要跑去浏览函数定义。
4.不声明当然可以,只要你将要使用的这个函数放在调用它的函数之前,而在函数定义之前,你将不能使用这个函数。如果这么做,意味着整个文件的前面部分将会全是函数定义,这无疑会让人抓狂,而声明只需要一行声明一下就可以了,简单明了。

2.函数的形参和返回值可以是什么数据类型?

C++对于返回值的类型有一定的限制:不能是数组,但可以是其他任何类型——整型、指针、浮点、结构和对象,但是有趣的是我们可以使用指针来指向数组元素,并且返回指针,但是并不能返回整个数组。
数组作为形参的方法,一种是function(int a[]); 另一种是function(int *a),但是数组作为形参和一般的数据类型作为形参不同。一般的数据类型作为形参,是函数在栈中为形参对应的实参创建了一个副本的内存并且将其实参内容保存进去,如果函数需要对数据内容进行修改的操作,是在这个拷贝的副本上进行的,而不是在原数据上进行。但是数组作为形参,函数如果要修改数组内容是直接在原数组上进行修改。

3.结构体作为函数形参有什么特点?

结构体虽然和数组一样,都可以存储多个数据项,但是在涉及到函数时,结构变量的行为更接近于一个基本的单值变量,也就是说,与数组不同,结构将其数据组合成单个实体或数据对象,该实体被视为一个整体。函数中参数为结构时,有三种方法:

1.直接将结构作为参数传递,并在需要时作为返回值返回,这种方式适合结构较小的情况,因为函数复制这种结构不会占用大量内存和时间。

struct travel_time  
{  
    int hours;  
    int mins;  
};  
travel_time sum(travel_time t1, travel_time t2) 
{
	...
}

2.传递结构的地址。这种方法适用于结构数据较大时

struct polar  
{  
    double distance;      // distance from origin  
    double angle;         // direction from origin  
};  
void show_polar (const polar * pda)  
{
	...
}

3.按引用传递结构体

struct a
{
	int data1;
	int data2;
	float data3;
};
void set_pc(a & ft);

按引用传递结构体,需要注意的是引用形参名是实参的一个别名,所以函数在代码块中对其的一切修改都是在原结构体上进行的,所以一定不要将函数的自动变量设置为返回值,因为函数结束时会自动删除自动变量所在的内存。如以下代码所示,将ft赋给newguy,但是当函数结束时newgauy不复存在了,所以函数找不到返回值。

const free_throws & clone2(free_throws & ft)
{
   free_throws newguy;
   newguy = ft;
   return newguy;
}

4.函数原型的语法需要注意的点

1.函数原型不要求提供变量名,有数据类型列表就足够了。

void cheers(int);//		不要忘记分号

2.原型中的变量名相当于占名符,不必与函数定义中的变量名相同。

double cube(double x);

3.编译器判断函数重载是根据函数形参的数据类型列表来的,和变量名没有实质性的关系。

5.原型有什么功能?

1.编译器正确处理函数返回值
2.编译器检查使用的参数数目是否正确
3.编译器检查使用的参数类型是否正确。如果不正确,则转换为正确的类型(在编译规则允许范围内)

6.函数和数组的关系

int sum_arr(int arr[], int n); //函数原型
int sum = sum_arr(ArrName,ArrSize)	//函数定义 ArrName是数组名

因为c++编译规则要求函数的形参和实参的数据类型要一致,其中实参为ArrName是数组名,根据C++规则,ArrName是一个指针,指向数组的第一个元素,因此形参对应的数据类型也需要为一个指针,所以我们可以也这样定义函数

int sum_arr(int* arr, int n); //函数原型
int sum = sum_arr(ArrName,ArrSize)	//函数定义 ArrName是数组名

之所以用数组表示法int arr[],就是为了表示arr不仅是arr数组第一个元素的地址,更是表示arr是一个int类型的数组名,它在某种程度上可以表示一个数组。
另外需要注意的是,数组表示法和指针表示法只有在数组问题上他们的含义相同,但在其他情况并不允许相互转换,如果指针指向一个独立的值,那么只能使用指针表示法。

7.数组作为参数按值传递时,传递的到底是什么?

我们已经知道,将数组作为参数,函数并不复制一个数组的副本,而是直接使用数组,那么说明并没有传递整个数组的内容,那传递的到底是什么呢?
实际上,函数仍然传递了一个值,但这个值是数组名,它在一般情况下表示的是数组第一个元素的地址而不是数组的内容。我们知道数组名除了表示数组第一个元素的地址外,在某种情况下也表示数组的地址。所以在这里数组名表示了数组的地址。
否则如果将一个包含几万个元素的数组按值传递给函数,系统开销会非常大,程序不仅需要更多的计算机内存,还需要花费大量的时间来复制大块的数据。

8.const关键字有什么用?

由以上问题我们知道,函数使用的是数组的原始数据,这在某种程度上增加了破坏数组的风险,所以当我们不需要修改数组的时候,需要用到一种保护数组的措施。
所以C++为了防止函数无意中修改数组的内容,可在声明形参时使用const关键字。

void show_array(const double ar[],int n);	//const关键字表示ar指向一个常量
1.const关键字意味着不能使用ar指针修改数组数据,也就是说可以使用数组的值,比如通过ar[0],但是不能通过ar指针来修改数组数据,比如ar[0] = 12345;这将是不允许的。

但是我们知道ar数组仍然是可以修改的,只是不能通过ar指针来修改。另外ar指针也是可以更改的,可以修改它指向的内存地址,不要混淆。
但是如果是以下这种情况

double * const pr = &scores;
这种情况表示pr指针只能指向scores所在的内存单位,这样的话就不能修改它指向的内存地址了,但是可以通过它修改scores的值:
*pr = 20;
还有就是C++禁止将const的地址赋给非const指针,const变量可以接受非const变量和const变量,但是不能将cosnt变量的值赋给非const变量,这样做没有意义。

9.函数和二维数组的关系

有以下代码:

int data[3][4]={{1,2,3,4},{2,3,4,5},{3,4,5,6}};
int total = sum(data,3);

那么sum函数的原型是什么样的?

int sum(int (*ar)[4],int size);

我们由data数组知道,data表示一个指针,指向二维数组中的第一个元素,二维数组中的第一个元素是一个有4个int类型元素的数组,这表示着data是一个指向4个int类型的数组指针。
那么我们可以认为

int (*ar)[4]={1,2,3,4};

这样函数的形参和实参的数据类型就对应上了。ar就表示一个数组指针。

10.函数指针

与其他的数据类型相似,函数也有地址,函数的地址是存储其机器语言代码的内存的开始地址。
很简单的是,函数的名称就是指向函数地址的指针,这和数组一致。

void estimate (int lines, double (*pf) (int));	//函数原型
estimate(50,pam);	//pam为函数名

那么怎么声明一个指针,指向一个函数呢?可以由以下这样

const double * f1(const double ar[],int n);	//原型
const double *(*p1)(cosnt double ar[] ,int n)  =f1;		//初始化
发布了22 篇原创文章 · 获赞 2 · 访问量 481

猜你喜欢

转载自blog.csdn.net/weixin_42709632/article/details/103946945