IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> ?关于C++之函数与指针 -> 正文阅读

[C++知识库]?关于C++之函数与指针

执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行完函数代码(也许还需将返回值放入到寄存器中),然后跳回到地址被保存的指令处。这与阅读文章时停下来看脚注,并在阅读完脚注后返回到以前阅读的地方类似。

与数据项相似,函数也有地址。函数的地址是存储其机器语言代码的内存的开始地址。

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<Enter>
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 };
	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
	cout << (*pc)[0](av, 3) << ": " << *(*pc)[0](av, 3) << endl;
	p_fun (*pd)[3] = &pa;// slightly harder way to declare pd(1)
	const double* (*(*pd)[3])(const double *, int) = &pa;// hard way to declare pd(2)
	const double* pdb = (*pd)[1](av, 3);
	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

代码里面最难理解的点在于如下这句代码:

p_fun pa[3] = { f1,f2,f3 };
const double* (*(*pd)[3])(const double *, int) = &pa;

首先,运算符优先级:[ ] > () > *
T (*pd)[3](K)——pd是一个指针(函数指针),pd指向一个包含3个元素的数组,每个数组元素都是具有参数列表为K,返回类型为T特征的函数。由于函数名只可以作为指针(函数指针)使用,而不可能有其他函数名用法,所以此声明是无效的!在此只是作个比较说明而已。
T (*(*pd)[3])(K)——pd是一个指针(函数指针),pd指向一个包含3个元素的数组,每个数组元素都是具有参数列表为K,返回类型为T特征的指针(函数指针)。第一个星号作用于pd变量,第二个星号作用于数组元素。

以上图片是去掉最外层的*(),其就不能作为函数指针数据了,在此无任何意义。

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

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

可以看到auto的强大,让程序员将主要精力放在设计而不是细节上。

注意到程序中:
p_fun pa[3] = { f1,f2,f3 };// f1、f2、f3都是函数名,也是代表着地址,都是指针变量
auto pb = pa;
cout << pa[i](av, 3);
对比:
auto pc = &pa;
cout << (*pc)[0](av, 3)
————————————
通过IDEA的提示,如下图,更直观比较pb与pc的赋值区别:

auto pb = pa;——对于数组pa是数组首元素地址,这时pb[0]=f1, pb[1]=f2, pb[2]=f3
auto pc = &pa;——对于数组&pa是整个数组的地址,两边加星号,则*pc=pa, (*pc)[0]=pa[0]=f1, (*pc)[1]=f2, (*pc)[2]=f3

下面这个示例,非常直观解释了以上疑惑:

int arr[3] = {3, 5, 7};
int (*pc)[3] = &arr;
cout << arr[0] << "," << arr[1] << "," << arr[2] << endl;
cout << pc[0] << "," << pc[1] << "," << pc[2] << endl;
cout << *pc[0] << "," << *pc[1] << "," << *pc[2] << endl;
cout << (*pc)[0] << "," << (*pc)[1] << "," << (*pc)[2];
3,5,7
0x3bc51ffafc,0x3bc51ffb08,0x3bc51ffb14
3,-987759876,59
3,5,7

arr是数组首元素地址,步长为1,所以可以直接下标访问。
pc被赋值为整个数组的地址,通过例子打印的地址也可佐证它步长为3,所以除了pc[0]外,其他下标访问的元素是无意义的。
pc指向整个数组的地址&arr,那(*pc)就恒等于arr了,这时(*pc)就是首元素地址了,可以直接用下标访问元素。

还可以看到:使用typedef,使程序编写简单很多:

typedef const double* (*p_fun)(const double*, int);
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-04 15:19:49  更:2022-03-04 15:19:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:35:01-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码