函数通过将返回值复制到指定的CPU寄存器或内存单元中来将其返回。随后,调用程序将查看该内存单元。 返回函数和调用函数必须就该内存单元中存储的数据的类型达成一致。函数原型将返回值类型告知调用程序,而函数定义命令被调用函数应返回什么类型的数据。
//arrfun1.cpp -- functions with an array argument
#include <iostream>
const int ArSize = 8;
int sum_arr(int* arr, int n);
int main() {
using namespace std;
int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };
//some systems require preceding int with static to enable array initialization
int sum = sum_arr(cookies, ArSize);
cout << "Total cookies eaten: " << sum << "\n";
return 0;
}
//return the sum of an integer array
int sum_arr(int* arr, int n) {
int total = 0;
for (int i = 0; i < n; i++) {
total += arr[i];
}
return total;
}
上述代码中,实际并没有将数组内容传递给函数,而是将数组的位置(地址)、包含的元素种类(类型)以及元素数目(n变量)提交给函数。函数可以使用原来的数组。传递常规变量时,函数将使用该变量的拷贝。
为了防止函数无意中修改数组的内容,可在声明形参时使用关键字const:
void show_array(const double ar[], int n){
using namespace std;
for(int i = 0; i < n; i++){
cout << "Property #" << (i + 1) << ": $";
cout << ar[i] << endl;
}
}
该声明表名,指针ar指向的是常量数据。
//arrfun4.cpp -- functions with an array range
#include <iostream>
const int ArSize = 8;
int sum_arr(const int* begin, const int* end);
int main() {
using namespace std;
int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };
//some systems require preceding int with static to enable array initialization
int sum = sum_arr(cookies, cookies + ArSize);
cout << "Total cookies eaten: " << sum << "\n";
sum = sum_arr(cookies, cookies + 3);
cout << "First three eaters ate: " << sum << " cookies.\n";
sum = sum_arr(cookies + 4, cookies + 8);
cout << "Last four eaters ate " << sum << " cookies.\n";
return 0;
}
//return the sum of an integer array
int sum_arr(const int* begin, const int* end) {
const int* pt;
int total = 0;
for (pt = begin; pt != end; pt++) {
total += *pt;
}
return total;
}
指针和const
可以用两种不同的方式将const关键字用于指针。
- 让指针指向一个常量对象,防止使用该指针修改所指向的值
- 将指针本身声明为常量,防止改变指针指向的位置
int age = 33;
const int * p = &age;
p 指向一个 const int ,因此不能使用 p 来修改这个值。
*pt += 1; // invalid
禁止将 const 的地址赋给常规指针
const float g_moon = 1.63;
float * p = &g_moon; // invalid
如果将g_moon的地址赋给指针p,则可以用p修改g_moon的值,这使得g_moon的const 状态很荒谬。
将指针指向指针,情况更复杂
int age = 39;
int * pd = &age; // *pd = 41 is a valid operation
const int * pt = pd; // *pt = 42 is an invaid operation
int age = 39;
const int* pt = &age;
这里的 const 只能防止 修改pt指向的值(这里为39),而不能防止修改pt的值。可以将一个新的地址赋给pt
int sage = 80;
pt = &sage; //okay to point to another location
但不能用pt来修改它指向的值(现在为80)
这种使用const 的方式使得无法修改指针的值:
int sloth = 30;
const int * ps = &sloth; // a pointer to const int
int * const finger = &sloth; // a const pointer to int
最后一种声明格式使得finger只能指向sloth,但允许使用finger修改sloth的值。
中间的声明,不允许使用ps来修改sloth的值,但允许将ps指向另一个位置。
*总之,finger 和 * ps都是 const, 而 finger 和 ps不是。 **
如果愿意,还可以声明指向const 对象的const 指针:
double trouble = 2.0E30;
const double * const stick = &trouble;
stick 只能指向trouble , 而且 stick 不能用来修改trouble的值。 总之,*stick 和 stick都是const。
通常,将指针作为函数参数来传递时,可以使用指向const的指针来保护数据。例如,
void show_array(const double ar[], int n);
在该声明中使用const意味着show_array()不能修改传递给它的数组中的值。只要只有一层间接关系,就可以使用这种技术。例如,这里的数组元素是基本类型,但如果它们是指针或指向指针的指针,则不能使用const。
函数和二维数组
int data[3][4] = {{1,2,3,4}, {9,8,7,6}, {2,4,6,8}};
int total = sum(data, 3);
data的类型是指向由4个int组成的数组的指针,因此正确的原型如下:
int sum(int (*ar)[4], int size);
或者
int sum(int ar[][4], int size);
指针类型指出,它指向四个int组成的数组。因此,指针类型指定了列数,这就是没有将列数作为独立的函数参数进行传递的原因。
int sum(int ar2[][4], int size){
int total = 0;
for(int r = 0; r < size; r++)
for(int c = 0; c < 4; c++)
total += ar2[r][c]
return total;
}
由于 ar2 指向数组(它的元素是由4个int组成的数组)的第一个元素(元素0),因此表达式ar2 + r 指向编号为 r的元素。因此 ar2[r] 是编号为 r的元素。由于该元素本身就是一个由 4 个 int组成的数组,因此 ar2[r] 是由4个int组成的数组的名称。
函数和结构
传递和返回结构
当结构比较小时,按值传递结构最合理,下面来看两个使用这种技术的示例。
//travel.cpp -- using structure with functions
#include <iostream>
struct travel_time {
int hours;
int mins;
};
const int Mins_per_hr = 60;
travel_time sum(travel_time t1, travel_time t2);
void show_time(travel_time t);
int main() {
using namespace std;
travel_time day1 = { 5, 45 };
travel_time day2 = { 4, 55 };
travel_time trip = sum(day1, day2);
cout << "Two-day total: ";
show_time(trip);
travel_time day3 = { 4, 32 };
cout << "Three-day total: ";
show_time(sum(trip, day3));
return 0;
}
travel_time sum(travel_time t1, travel_time t2) {
travel_time total;
total.mins = (t1.mins + t2.mins) % Mins_per_hr;
total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr;
return total;
}
void show_time(travel_time t) {
using std::cout;
cout << t.hours << " hours, "
<< t.mins << " minutes\n";
}
传递结构的地址
//strctfun.cpp -- functions with a structure argument
#include <iostream>
#include <cmath>
//structure declarations
//极坐标
struct polar {
double distance;
double angle;
};
//直角坐标
struct rect {
double x;
double y;
};
void rect_to_polar(const rect* pxy, polar* pda);
void show_polar(const polar* pda);
int main() {
using namespace std;
rect rplace;
polar pplace;
cout << "Enter the x and y values: ";
while (cin >> rplace.x >> rplace.y){
rect_to_polar(&rplace, &pplace);
show_polar(&pplace);
cout << "Next two numbers (q to quit): ";
}
cout << "Done.\n";
return 0;
}
//show polar coordinates,converting angle to degrees
void show_polar(const polar* pda) {
using namespace std;
// 180 / pi = 57.29577951
const double Red_to_deg = 57.29577951;
cout << "distance = " << pda->distance;
cout << ", angle = " << pda->angle * Red_to_deg;
cout << " degrees\n";
}
//convert rectangular to polar coordinates
void rect_to_polar(const rect* pxy, polar* pda) {
pda->distance = sqrt(pxy->x * pxy->x + pxy->y * pxy->y);
pda->angle = atan2(pxy->y, pxy->x);
}
函数与array对象
#include <iostream>
#include <array>
#include <string>
//constant data
const int Seasons = 4;
const std::array<std::string, Seasons> Snames = {
"Spring", "Summer", "Fall", "Winter"
};
//function to modify array object
void fill(std::array<double, Seasons>* pa);
void show(std::array<double, Seasons> da);
int main() {
std::array<double, Seasons> expenses;
fill(&expenses);
show(expenses);
return 0;
}
void fill(std::array<double, Seasons>* pa) {
for (int i = 0; i < Seasons; i++) {
std::cout << "Enter " << Snames[i] << " expenses: ";
std::cin >> (*pa)[i];
}
}
void show(std::array<double, Seasons> da) {
double total = 0.0;
std::cout << "\nEXPENSES\n";
for (int i = 0; i < Seasons; i++) {
std::cout << Snames[i] << ": $" << da[i] << std::endl;
total += da[i];
}
std::cout << "Total Expenses: $" << total << std::endl;
}
递归函数
#include <iostream>
const int Len = 66;
const int Divs = 6;
void subdivide(char ar[], int low, int high, int level);
int main() {
char ruler[Len];
int i;
for (i = 1; i < Len - 2; i++) {
ruler[i] = ' ';
}
ruler[Len - 1] = '\0';
int max = Len - 2;
int min = 0;
ruler[min] = ruler[max] = '|';
std::cout << ruler << std::endl;
for (i = 1; i <= Divs; i++) {
subdivide(ruler, min, max, i);
std::cout << ruler << std::endl;
for (int j = 1; j < Len - 2; j++) {
ruler[j] = ' ';
}
}
return 0;
}
void subdivide(char ar[], int low, int high, int level) {
if (level == 0) {
return;
}
int mid = (low + high) / 2;
ar[mid] = '|';
subdivide(ar, low, mid, level - 1);
subdivide(ar, mid, high, level - 1);
}
函数指针
假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都将使用该函数。对于所有用户来说,estimate中一部分代码都是相同的,但该函数允许每个程序员提供自己的算法来估算时间。为实现这种目标,采用的机制是,将程序员要使用的算法函数的地址传递给estimate()。为此必须完成下面的工作:
- 获取函数的地址
- 声明一个函数指针
- 使用函数指针来调用函数
1、获取函数地址
获取函数的地址很简单:只要使用函数名(后面不跟参数)即可。例如,think()是一个函数,则think是该函数的地址。
2、声明函数指针
double pam(int); // prototype
则正确的指针类型声明如下:
double (*pf)(int);
/*
*pf(int) 意味着 pf()是一个返回指针的函数,而(*pf)(int)意味着pf是一个指向函数的指针
*/
double *pf(int);
正确声明pf之后,便可以将相应的函数的地址赋给它:
double pam(int);
double (*pf)(int);
pf = pam; //pf now points to the pam() function
pam()的特征标和返回类型必须与pf相同。如果不相同,编译器将拒绝这种赋值:
double ned(double);
int ted(int);
double (*pf)(int);
pf = ned; //invalid -- mismatched signature
pf = ted; //invalid -- mismatched return types
现在回过头看一下前面提到的estimate()函数。假设要将将要编写的代码行数和估算算法(如pam()函数)的地址传递给它,则其原型将如下:
//第二个参数是一个函数指针,它指向的函数接受一个int参数,并返回一个double 值
void estimate(int lines, double (*pf)(int));
estimate(50, pam); // function call telling estimate() to use pam()
//fun_ptr.cpp -- pointers to functions
#include <iostream>
double betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));
int main() {
using namespace std;
int code;
cout << "How many lines of code do you need? ";
cin >> code;
cout << "Here's Betsy's estimate:\n";
estimate(code, betsy);
cout << "Here's Pam's estimate:\n";
estimate(code, pam);
return 0;
}
double betsy(int lns) {
return 0.05* lns;
}
double pam(int lns) {
return 0.03* lns + 0.0004 * lns * lns;
}
void estimate(int lines, double (*pf)(int)) {
using namespace std;
cout << lines << " lines will take ";
cout << (*pf)(lines) << " hour(s)\n";
}
深入探讨函数指针
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
接下来,假设要声明一个指针,他可指向这三个函数之一。假定该指针名为pa,则只需将目标函数原型中的函数名替换为 (*pa):
const double * (*pa) (const double* , int);
//初始化
const double * (*pa) (const double* , int) = f1;
//也可使用c++11的自动类型推断功能
auto p2 = f2;
声明一个函数指针数组
const double * (*pa[3]) (const double * , int) = {f1,f2,f3};
pa 是一个包含三个元素的数组,而要声明这样的数组,首先需要使用 pa[3]。 运算符[] 的 优先级高于 * ,因此*pa[3] 表名 pa 是一个包含三个指针的数组。每个指针指向的是,参数为( const double *,int), 且返回类型为 const double * 的函数。
const double * px = pa[0](av, 3);
const double * py = (*pa[1])(av, 3);
//arfupt.cpp -- an array of function pointers
#include <iostream>
const double* f1(const double ar[], int n);
const double* f2(const double[], int);
const double* f3(const double*, int);
int main() {
using namespace std;
double av[3] = { 1112.3, 1542.6, 2227.9 };
//pointer to a function
const double* (*p1) (const double*, int) = f1;
auto p2 = f2;
cout << "Using pointers to functions:\n";
cout << " Address Value\n";
cout << (*p1)(av, 3) << ": " << *(*p1)(av, 3) << endl;
cout << p2(av, 3) << ": " << *p2(av, 3) << endl;
//pa an array of pointers
//auto doesn't work with list initialization
const double* (*pa[3]) (const double*, int) = { f1,f2,f3 };
auto pb = pa;
cout << "\nUsing an array of pointers to functions:\n";
cout << " Address Value\n";
for (int i = 0; i < 3; i++) {
cout << pa[i](av, 3) << ": " << *pa[i](av, 3) << endl;
}
cout << "\nUsing a pointer to a pointer to a function:\n";
cout << " Address Value\n";
for (int i = 0; i < 3; i++) {
cout << pb[i](av, 3) << ": " << *pb[i](av, 3) << endl;
}
//what about a pointer to an array of function pointers
cout << "\nUsing a pointer to a pointer to a function:\n";
cout << " Address Value\n";
auto pc = &pa;
//const double* (*(*pc)[3]) (const double*, int) = &pa;
cout << (*pc)[0](av, 3) << ": " << *(*pc)[0](av, 3) << endl;
//hard way to declare pd;
const double* (*(*pd)[3]) (const double*, int) = &pa;
//store return value in pdb
const double* pdb = (*pd)[1](av, 3);
cout << pdb << ": " << *pdb << endl;
//alternative notation
cout << (*(*pd)[2])(av, 3) << ": " << *(*(*pd)[2])(av, 3) << endl;
return 0;
}
const double* f1(const double* ar, int n) {
return ar;
}
const double* f2(const double ar[], int n) {
return ar + 1;
}
const double* f3(const double ar[], int n) {
return ar + 2;
}
注:指向函数指针数组的指针并不少见
使用typedef进行简化
typedef const double *(*p_fun)(const double *, int);// p_fun now a type name
p_fun p1 = f1; // p1 points to the f1() function
//然后使用这个别名来简化代码
p_fun pa[3] = {f1, f2, f3}; // pa an array of 3 function pointers
p_fun (*pd)[3] = &pa; // pd points to an array of 3 funciton pointers