上一篇讲述了关于指针和结构的关系,这次准备讲一下指针和函数的一些用法:
1.作为函数参数的指针:
当函数的原型声明指针作为形参时,调用者必须提供一个指针变量或者一个地址值作为实参。
声明指针作为形参的格式有2种:
void ErrorMessage(char* msg);
void ErrorMessage(char msg[]);
这两个原型声明的作用是一样的。组数不能作为函数的参数的。上面第一个格式说明形参时一个字符型指针,第二种格式说明形参时一个指向字符型数组的指针。他们两者没有任何区别。
另外:如果声明的指针形参时带有元素个数说明的数组形式,编译器会忽略元素个数的说明。下面这个原型声明也和前面两个等效:
void ErrorMessage(char msg[25]);
对于这3个原型说明,编译器都理解为形参是一个字符型指针。至于使用哪一种就看个人喜好了。
传递的实参既可以是指针变量,也可以是指针指向的数据类型的变量的地址。
调用的方法如下所示:
注意:
上面这段代码可能在编译时出现如下警告:warning: deprecated conversion from string constant to 'char*'
为什么呢?原来char *背后的含义是:给我个字符串,我要修改它。
而理论上,我们传给函数的字面常量是没法被修改的。
所以说,比较和理的办法是把参数类型修改为const char *。
这个类型说背后的含义是:给我个字符串,我只要读取它。
修改的方法有多种,譬如:
对于指向多维数组的指针参数,如果函数将遍历数组,那么必须给出最内层的元素个数说明,即原型声明必须告诉编译器最内层数组的长度。
运行结果如下:
如果像下面这样声明DisplayCalendar函数结果也是一样的:
void DisplayCalendar(int* cal[7]); //参考:再议C/C++中关于指针、地址和多维数组的关系
另外:
本例中包含了<iomanip>头文件,该头文件包含了流操纵符的声明。上诉代码中使用std::setw操纵符来控制屏幕显示的格式。其参数指定了流中下一个对象显示输出时所占的以字符为单位的最小宽度。
2.以地址作为函数的返回值
当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用的函数,以用于需要指针或地址的表达式中。示例如下:
运行结果如下:
注意:
main()函数中第二个cout语句,它调用了函数GetDate(),并使用了一个指针运算符,这将得到函数返回的地址,并把该地址所存放的整数值传递给cout对象。同样,也可以把返回值赋给一个指针变量,然后利用这个指针遍历数组,示例如下:
运行结果如下:
注意:上述代码中有一个小错误。当输入的wk和dy导致计算出来的下标越过了数组的终止元素-1时,循环将显示在有效的地址中发现的任何数值,直到碰巧出现-1为止。如果碰到这种情况,很可能只有通过强行终止循环来退出程序。不妨想一下如何避免这种情况的发生。
3.指向函数的指针
指向函数的指针(即函数指针)包含了函数的地址,我们可以通过它来调用函数。声明函数指针的格式如下:
int (*fptr)();
该指针变量的名字为fptr。这个特殊的指针指向一个返回整型值,无任何参数的函数。指针的声明必须和它指向的函数的声明保持一致。
指针名和指针运算符(*)外面的圆括号改变了默认的运算符优先级。如果没有圆括号,将变成一个返回整型指针的函数的原型声明。
把函数的地址赋给函数指针,可以采用以下两种形式:
fptr = &TheFunction;
fptr = TheFunction;
可见.取地址运算符不是必须的,因为单单一个函数标识符就表示了它的地址。如果是函数调用,则必须包含用一对圆括号括起来的参数表。
可以采用如下所示的任何一种格式来通过指针调用函数。
x = (*fptr)();
x = fptr();
上面的第二种形式看起来和函数调用无异。但是我们这次推荐使用第一种,因为它明确支出通过指针而非函数名来调用函数。
下面的代码说明了如何使用函数指针:
运行结果如下:
注意:函数指针在不同的时候可以具有不同的函数地址。
通过使用函数指针数组可以建立一个有限状态机,使得程序根据一个状态变量的值来决定自己的动作,确定下一步该调用哪个函数。有限状态机的一个例子就是表驱动菜单管理器。例子如下: