第7章-cpp函数——C++的编程模块

本章内容包括:
• 函数基本知识。
• 函数原型。
• 按值传递函数参数。
• 设计处理数组的函数。
• 使用const指针参数。
• 设计处理文本字符串的函数。
• 设计处理结构的函数。
• 设计处理string对象的函数。
• 调用自身的函数(递归)。
• 指向函数的指针。

C++对于返回值的类型有一定的限制:不能是数组,但可以是其他任何类型——整数、浮点数、指针,甚至可以是结构和对象!(有趣的是,虽然C++函数不能直接返回数组,但可以将数组作为结构或对象组成部分来返回。)

通常,函数通过将返回值复制到指定的CPU寄存器或内存单元中来将其返回。随后,调用程序将查看该内存单元。返回函数和调用函数必须就该内存单元中存储的数据的类型达成一致。函数原型将返回值类型告知调用程序,而函数定义命令被调用函数应返回什么类型的数据(参见下图)。在原型中提供与定义中相同的信息似乎有些多余,但这样做确实有道理。要让信差从办公室的办公桌上取走一些物品,则向信差和办公室中的同事交代自己的意图,将提高信差顺利完成这项工作的概率。

为何编译器需要原型,难道它就不能在文件中进一步查找,以了解函数是如何定义的吗?这种方法的一个问题是效率不高。编译器在搜索文件的剩余部分时将必须停止对main( )的编译。一个更严重的问题是,函数甚至可能并不在文件中。C++允许将一个程序放在多个文件中,单独编译这些文件,然后再将它们组合起来。在这种情况下,编译器在编译main( )时,可能无权访问函数代码。如果函数位于库中,情况也将如此。避免使用函数原型的唯一方法是,在首次使用函数之前定义它,但这并不总是可行的。另外,C++的编程风格是将main( )放在最前面,因为它通常提供了程序的整体结构。

函数参数和按值传递

double cube(duble x); // 函数原型

double volumn = cube(5); // 函数调用

被调用时,该函数将创建一个新的名为x的double变量,并将其初始化为5。这样,cube( )执行的操作将不会影响main( )中的数据,因为cube( )使用的是side的副本,而不是原来的数据。实现这种保护也可以使用const限定符。

 (注意:x是【复制】的值)

函数和数组

#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};
  int sum = sum_arr(cookies, ArSize);
  cout << "Total cookies eaten: " << sum << "\n";
  return 0;
}

int sum_arr(int arr[], int n) {
  int total = 0;
  for (int i = 0; i < n; i++) 
	  total = total + arr[i];
  return total;
}

在大多数情况下,C++和C语言一样,也将数组名视为指针,C++将数组名解释为其第一个元素的地址:cookies==&cookies[0]。方括号指出arr是一个数组,而方括号为空则表明,可以将任何长度的数组传递给该函数。但实际情况并非如此:arr实际上并不是数组,而是一个指针!好消息是,在编写函数的其余部分时,可以将arr看作是数组。可以在sum_arr函数的int arr[]替换为int * arr,同时也要在函数原型替换。

我们来看一看上面程序清单暗示了什么。函数调用sum_arr(coolies, ArSize)将cookies数组第一个元素的地址和数组中的元素数目传递给sum_arr( )函数。sum_arr( )函数将cookies的地址赋给指针变量arr,将ArSize赋给int变量n。这意味着,程序清单实际上并没有将数组内容传递给函数,而是将数组的位置(地址)、包含的元素种类(类型)以及元素数目(n变量)提交给函数(参见下图)。有了这些信息后,函数便可以使用原来的数组。传递常规变量时,函数将使用该变量的拷贝;但传递数组时,函数将使用原来的数组。实际上,这种区别并不违反C++按值传递的方法,sum_arr( )函数仍传递了一个值,这个值被赋给一个新变量,但这个值是一个地址,而不是数组的内容。

数组名与指针对应是好事吗?确实是一件好事。将数组地址作为参数可以节省复制整个数组所需的时间和内存。如果数组很大,则使用拷贝的系统开销将非常大;程序不仅需要更多的计算机内存,还需要花费时间来复制大块的数据。另一方面,使用原始数据增加了破坏数据的风险。在经典的C语言中,这确实是一个问题,但ANSI C和C++中的const限定符提供了解决这种问题的办法。

理解函数按值传递

#include <iostream>
const int ArSize = 8;
int sum_arr(int arr[], int n);
int main() {
  int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128};
  std::cout << cookies << " = array address, ";
  std::cout << sizeof cookies << " = sizeof cookies\n";

  int sum = sum_arr(cookies, ArSize);
  std::cout << "Total cookies eaten: " << sum << std::endl;
  sum = sum_arr(cookies, 3);  // a lie
  std::cout << "First three eaters ate " << sum << " cookies.\n";
  sum = sum_arr(cookies + 4, 4);  // another lie
  std::cout << "Last four eaters ate " << sum << " cookies.\n";
  return 0;
}

int sum_arr(int arr[], int n) {
  int total = 0;
  std::cout << arr << " = arr, ";
  std::cout << sizeof arr << " = sizeof arr\n";
  for (int i = 0; i < n; i++) 
	  total = total + arr[i];
  return total;
}
006FFCE8 = array address, 32 = sizeof cookies
006FFCE8 = arr, 4 = sizeof arr
Total cookies eaten: 255
006FFCE8 = arr, 4 = sizeof arr
First three eaters ate 7 cookies.
006FFCF8 = arr, 4 = sizeof arr
Last four eaters ate 240 cookies.

通过程序运行结果,我们知道传递进函数的的确是地址006FFCE8,从4 = sizeof arr也正说明了传递的是一个地址而不是整个数组,在调用函数外sizeof arr是数组长度,而函数里面sizeof arr是地址长度,更说明传递的仅仅是指针int * arr,没有作为数组名的性质了。一般地址是int型就是占4个字节。否则就像第一条打印结果一样size为32了。而后面调用sum_arr(cookies + 4, 4);从结果可以看到它的地址cookies+4和cookies的地址偏移了4*sizeof(int)=16,即006FFCE8+ 16(0x10) = 006FFCF8。总之,传递的是地址这个值。

如果想让数组内容只读,可以加const限定符:int sum_arr(const int arr[], int n);

使用数组区间的函数

上面的程序,要传递一个数组指针和数组的大小。还有另一种给函数提供所需信息的方法,即指定元素区间(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};
  int sum = sum_arr(cookies, cookies + ArSize);
  cout << "Total cookies eaten: " << sum << endl;
  sum = sum_arr(cookies, cookies + 3);  // first 3 elements
  cout << "First three eaters ate " << sum << " cookies.\n";
  sum = sum_arr(cookies + 4, cookies + 8);  // last 4 elements
  cout << "Last four eaters ate " << sum << " cookies.\n";
  return 0;
}

int sum_arr(const int* begin, const int* end) {
  const int* pt;
  int total = 0;
  for (pt = begin; pt != end; pt++) 
	  total = total + *pt;
  return total;
}
Total cookies eaten: 255
First three eaters ate 7 cookies.
Last four eaters ate 240 cookies.

使用const限定符,使数组元素只可读,避免了意外的修改了数组元素的值。

函数和二维数组

为编写将二维数组作为参数的函数,必须牢记,数组名被视为其地址,因此,相应的形参是一个指针,就像一维数组一样。比较难处理的是如何正确地声明指针。例如,假设有下面的代码:

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

为何sum()的原型将行数(3)作为参数而不是列数(4)呢?由于data是一个数组名,该数组有3个元素。每一个元素本身就是一个数组,由4个int值组成。因此要声明一个指向二维数组的指针,如下:

int (*ar2)[4];

由于优先级,括号必不可少。由于data的类型是指向由4个int组成的数组的指针,正确的函数原型如下:

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

另外还有一种格式,与上述原型含义完全相同,但可读性更强:

int sum(int ar2[][4], int size);

上述两个原型都指出,ar2是指针而不是数组。

ar2[r][c]是数组中的一个元素,是一个int值。必须对指针ar2执行两次解除引用,才能得到数据。对于指针ar2取数组元素的值最简单的方法是使用方括号两次:ar2[r][c]。然而,如果不考虑难看的话,也可以使用运算符*两次:(可参考C的:关于二维数组与指针

ar2[r][c] == *(*(ar2+r)+c) // 恒等

函数和结构

也可以按值传递结构,就像普通变量那样。在这种情况下,函数将使用原始结构的副本。另外,函数也可以返回结构。与数组名就是数组第一个元素的地址不同的是,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&。

传递和返回结构(也进一步理解函数按值传递参数):

#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};  // 5 hrs, 45 min
  travel_time day2 = {4, 55};  // 4 hrs, 55 min

  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 namespace std;
  cout << t.hours << " hours, " << t.mins << " minutes\n";
}

C++函数按值传递参数,因此函数调用show_time(sum(trip, day3))将执行函数调用sum(trip, day3),以获得其返回值。然后,show_time( )调用将sum( )的返回值(而不是函数自身)传递给show_time( )。

传递结构的地址:

传递结构的地址而不是整个结构可以节省时间和空间,这样需要重新编写前面的函数,使用指向结构的指针。重新编写sum( )函数,如下:

travel_time * sum(const travel_time * pt1, const travel_time * pt2) {
  const travel_time * ptotal;
  total->mins = (pt1->mins + pt2->mins) % Mins_per_hr;
  total->hours = pt1->hours + pt2->hours + (pt1->mins + pt2->mins) / Mins_per_hr;
  return ptotal;
}

主调用程序调用函数时,将结构地址&day1、&day2而不是结构本身day1、day2传递给它。由于不应该修改结构,因此使用了const修饰符。由于形参是指针而不是结构,因此使用间接成员运算符->,而不是成员运算符.。

函数与string对象

string对象与结构的更相似。例如,可以将一个结构赋给另一个结构,也可以将一个对象赋给另一个对象。可以将结构作为完整的实体传递给函数,也可以将对象作为完整的实体进行传递。如果需要多个字符串,可以声明一个string对象数组,而不是二维char数组。

#include <iostream>
#include <string>
using namespace std;
const int SIZE = 5;
void display(const string sa[], int n);
int main() {
  string list[SIZE];  // an array holding 5 string object
  cout << "Enter your " << SIZE << " favorite astronomical sights:\n";
  for (int i = 0; i < SIZE; i++) {
    cout << i + 1 << ": ";
    getline(cin, list[i]);
  }
  cout << "Your list:\n";
  display(list, SIZE);
  return 0;
}

void display(const string sa[], n) {
  for (int i = 0; i < n; i++) 
	  cout << i + 1 << ": " << sa[i] << endl;
}

函数与array对象

#include <array>
#include <iostream>
#include <string>
using namespace std;
const int SEASONS = 4;
const array<string, SEASONS> Snames = {"Spring", "Summer", "Fall", "Winter"};
void fill(array<double, SEASONS>* pa);
void show(array<double, SEASONS> da);
int main() {
  array<double, 4> expenses;
  fill(&expenses);
  show(expenses);
  return 0;
}

void fill(array<double, SEASONS>* pa) {
  for (int i = 0; i < SEASONS; i++) {
    cout << "Enter " << Snames[i] << " expenses: ";
    cin >> (*pa)[i];
  }
}

void show(array<double, SEASONS> da) {
  double total = 0.0;
  cout << "\nEXPENSES\n";
  for (int i = 0; i < SEASONS; i++) {
    cout << Snames[i] << ": $" << da[i] << '\n';
    total += da[i];
  }
  cout << "Total: $" << total << '\n';
}

为简化程序,并将重点放在函数可如何使用对象上,函数fill()没有检查输入是否有效。

函数fill()和show()都有缺点。函数show()存在的问题是,expenses存储了四个double值,而创建一个新对象并将expenses的值复制到其中(函数)的效率太低。如果修改该程序,使其处理每月甚至每日的开支,这种问题将更严重。

函数fill()使用指针来直接处理原始对象,这避免了上述效率低下的问题,但代价是代码看起来更复杂。

递归

#include <iostream>
using namespace std;
void countdown(int n);
int main() {
  countdown(4);
  return 0;
}

void countdown(int n) {
  cout << "Counting down ... " << n << "(n at " << &n << ")" << endl;
  if (n > 0) 
	  countdown(n - 1);  // function calls itself
  cout << n << ": Kaboom!........." << "(n at " << &n << ")" << endl;
}
Counting down ... 4(n at 0133F9E0)
Counting down ... 3(n at 0133F908)
Counting down ... 2(n at 0133F830)
Counting down ... 1(n at 0133F758)
Counting down ... 0(n at 0133F680)
0: Kaboom!.........(n at 0133F680)
1: Kaboom!.........(n at 0133F758)
2: Kaboom!.........(n at 0133F830)
3: Kaboom!.........(n at 0133F908)
4: Kaboom!.........(n at 0133F9E0)

每个递归调用都创建自己的一套变量,因此当程序到达第5次调用时,将有5个独立的n变量,其中每个变量的值都不同。在一个内存单元(内存地址为0133F9E0),存储的n值为4;在另一个内存单元(内存地址为0133F908),存储的n值为3;等等。另外,注意到在Counting down阶段和Kaboom阶段的相同层级,n的地址相同。

包含多个递归调用的递归:

// using recursion to subdivide a ruler
#include <iostream>
const int LEN = 66;
const int DIVS = 6;
void resetRuler(char* ruler);
void subdivide(char ar[], int low, int high, int level);
int main() {
  char ruler[LEN];
  resetRuler(ruler);
  ruler[LEN - 1] = '\0';
  int max = LEN - 2;
  int min = 0;
  ruler[min] = ruler[max] = '|';
  std::cout << ruler << std::endl;
  for (int i = 1; i <= DIVS; i++) {
    subdivide(ruler, min, max, i);
    std::cout << ruler << std::endl;
    resetRuler(ruler);
  }
  return 0;
}

void subdivide(char ar[], int low, int high, int level) {
  if (level == 0) 
	return;
  int mid = (high + low) / 2;
  ar[mid] = '|';
  subdivide(ar, low, mid, level - 1);
  subdivide(ar, mid, high, level - 1);
}

void resetRuler(char* ruler) { // reset to blank ruler
  for (int i = 1; i < LEN - 2; i++) 
	  ruler[i] = ' ';
}
|                                                               |
|                               |                               |
|               |               |               |               |
|       |       |       |       |       |       |       |       |
|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

程序中subdivide( )函数使用变量level来控制递归层。函数调用自身时,将把level减1,当level为0时,该函数将不再调用自己。注意,subdivide( )调用自己两次,一次针对左半部分,另一次针对右半部分。最初的中点被用作一次调用的右端点和另一次调用的左端点。

请注意,调用次数将呈几何级数增长。也就是说,调用一次导致2^1=2个调用,然后导致2^2=4个调用,再导致2^3=8个调用,依此类推。这就是6层调用能够填充2^6=64个元素的原因。这将不断导致函数调用数(以及存储的变量数)翻倍,因此如果要求的递归层次很多,这种递归方式将是一种糟糕的选择;然而,如果递归层次较少,这将是一种精致而简单的选择。

函数指针

与数据项相似,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。通常,这些地址对用户而言,既不重要,也没有什么用处,但对程序而言,却很有用。例如,可以编写将另一个函数的地址作为参数的函数。这样第一个函数将能够找到第二个函数,并运行它。与直接调用另一个函数相比,这种方法很笨拙,但它允许在不同的时间传递不同函数的地址,这意味着可以在不同的时间使用不同的函数。

首先通过一个例子来阐释这一过程。假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都将使用该函数。对于所有的用户来说,estimate()中一部分代码都是相同的,但该函数允许每个程序员提供自己的算法来估算时间。为实现这种目标,采用的机制是,将程序员要使用的算法函数的地址传递给estimate()。

double pam(int); // 函数原型

double (*pf)(int); // 声明函数指针(参数为int型,返回值为double型)

pf = pam; // 函数名就是函数的地址,这时pf就是指向函数pam()的指针(参数、返回值类型都匹配)

double x = pam(4); // 用函数名调用
double x = (*pf)(5); // 使用函数指针调用

为何 pam 和 (*pf) 等价呢?由于pf是函数指针,那*pf是函数,因此可将(*pf)( )用作函数调用。

#include <iostream>
using namespace std;
double betsy(int);
double pam(int);
void estimate(int lines, double (*pf)(int));
int main() {
	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)) {
	cout << lines << " lines will take ";
	cout << (*pf)(lines) << " hour(s)\n";
}
How many lines of code do you need? |100
Here's Betsy's estimate:
100 lines will take 5 hour(s)
Here's Pam's estimate:
100 lines will take 7 hour(s)

可以看到,调用相同的estimate()函数,有不同的效果,这都是函数指针的功劳,它可以按需实现不同的功能。

#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 };

	typedef const double* (*p_fun)(const double*, int); // 创建类型别名
	p_fun p1 = f1;
	auto p2 = f2;  // C++0x automatic type deduction
	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;

	p_fun pa[3] = { f1,f2,f3 }; // 指针数组,所以&pa才是步长一致的首地址。若把首地址作pa的话,步长不一致!
	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;

	cout << "\nUsing pointers to an array of pointers:\n";
	cout << " Address  Value\n";
	auto pc = &pa; // easy way to declare pc,not:auto pc = pa; !!!
	cout << (*pc)[0](av, 3) << ": " << *(*pc)[0](av, 3) << endl; // 优先级:arr[]>()>*
	p_fun (*pd)[3] = &pa;// slightly harder way to declare pd
	// hard way to declare pd
	const double *(*(*pd)[3])(const double *, int) = &pa;
	const double* pdb = (*pd)[1](av, 3); // store return value in pdb
	cout << pdb << ": " << *pdb << endl;
	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;
}
Using pointers to functions:
 Address  Value
006FFC0C: 1112.3
006FFC14: 1542.6

Using an array of pointers to functions:
 Address  Value
006FFC0C: 1112.3
006FFC14: 1542.6
006FFC1C: 2227.9

Using a pointer to a pointer to a function:
 Address  Value
006FFC0C: 1112.3
006FFC14: 1542.6
006FFC1C: 2227.9

Using pointers to an array of pointers:
 Address  Value
006FFC0C: 1112.3
006FFC14: 1542.6
006FFC1C: 2227.9

对于函数原型f1、f2、f3这些函数的特征标看似不同,但实际上相同。首先,在函数原型中,参数列表const double ar [ ]与const double * ar的含义完全相同。其次,在函数原型中,可以省略标识符。因此,const double ar [ ]可简化为const double [ ],而const double * ar可简化为const double *。因此,上述所有函数特征标的含义都相同。

注:指针的指针的步长显然是和指针的步长是不一致的,一般指针的指针步长 > 指针的步长。

从运行结果,可以看到,同一函数的地址是固定不变的。

以下最后的3行代码功能一样,可以看到auto的强大,让程序员将主要精力放在设计而不是细节上。

1—— typedef const double* (*p_fun)(const double*, int);
2—— p_fun pa[3] = { f1,f2,f3 };

3—— p_fun (*pd)[3] = &pa; // typedef后的稍简化版
4—— const double* (*(*pd)[3])(const double *, int) = &pa; // C++98且不用typedef则要自己手写代码

对上面代码有些晦涩难理解的点进行解析:

行1:在下面typedef有解析。

行2:声明一个数组pa,它包含3个p_fun类型元素。

行3:假如左边换成: int(*pi)[3]就很快能明白pi是一个指向二维数组的指针,每一维元素是3个int类型。同样的p_fun(*pd)[3]即表明pd是一个指向二维数组的指针,每一维元素是3个p_fun类型。&pa是什么意思呢?&pa就是整个数组的地址,但它的值与pa的值是相等的,不过步长是不相等的。关于这一点参考C的关于二维数组与指针里有详解析。但如果把pa直接赋值给p_fun(*pd)[3]是会在编辑器里就报错的,因为类型不匹配。一个简单的报错,如下图所示:

根据上面的报错信息,同理,“p_fun *”类型的值不能用于初始化“p_fun (*)[3]”类型的实体。所以步长要相等。pi是一个指向二维数组的指针,即它指的是一个一维数组的整个数组的地址,只有&pa才表示整个数组的地址,所以这肯定是不能用pa来替代的。剖析完毕。

行4:行3成立,然后用行1的typedef内容去替换掉行3中的p_fun,自然就成了行4了。函数原型是: const double* func(const double*, int);如果要变成&pa类型,因pa每个元素都是指针,即pa是一个指向指针数组的指针,&pa就是整个数组的地址。首先,func本身就是一个指针(函数指针),(*pd)[3]==pd[0][3],(*(*pd)[3])==*pd[0][3],表示pd是一个指向指针数组的指针。这样就和&pa类型完全匹配了,这时将func变成(*(*pd)[3])就可以被赋值&pa了。

p_fun(*pd)[3] = &pa;
// p_fun后面加个空格就好理解了,不加空格或加空格运行是一样的:
// p_fun (*pd)[3]:pd是一个指向3个元素类型为p_fun的数组指针
——————————————————————————————————————————————————————
const double *(*(*pd)[3])(const double *, int) = &pa;
// 一步步拆解就明了含义了:
// (*pd)[3]:pd是一个指向3个元素数组的指针
// (*(*pd)[3]):pd一个指向3个元素数组的指针【的指针】
// 这个数组的元素是函数类型:参数(const double *, int), 返回double *类型,pd还是个const指针

使用typedef进行简化

上面程序中:

typedef const double* (*p_fun)(const double*, int);

就表明p_fun是一个类型名,即一个参数列表为(const double*, int),返回为double *的一个函数const指针类型。

之后就可以直接把p_fun当一个类型来使用它:

p_fun p1 = f1; // p1指向函数f1()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值