C++函数指针
如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址我们就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针。
获取函数指针:函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名。
声明指针时,必须指定指针指向的数据类型,同样,声明指向函数的指针时,必须指定指针指向的函数类型,这意味着声明应当指定函数的返回类型以及函数的参数列表。
函数指针作为参数进行传递时:
void call(int(*callback)(int),int a) //函数指针作为参数传递的正确写法
void call(int *(callback)(int),int a) //这不是函数指针的声明,这种写法本质上是声明了一个函数叫做callback,其返回值是int数据类型的指针,就是所谓的指针函数。
使用函数指针调用函数:
#include <iostream>
int add(int a, int b)
{
return a + b;
}
void call(int (*callback)(int ,int ),int a ,int b)
{
std::cout << a << " b: " << b << std::endl;
std::cout << callback(a, b) << std::endl;
}
int main()
{
int a = 10;
int b = 20;
call(add, a, b); //函数指针作为参数传递
int (*p)(int, int); //声明一个函数指针
p = add; //使用指针调用函数,函数名就是函数地址
std::cout << (*p)(a, b) << std::endl; //结果是30
return 0;
}
例程2:
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
double* f4(double[], int n); //声明一个函数,返回值是double数据类型的指针
int main(int argc, char* argv[])
{
double a[3] = {
12.1, 3.4, 4.5 };
double* (*p)(double[], int n) = f4; //声明函数指针,指向f4
cout << *p(a, 0) << endl;
cout << *(*p)(a, 0) << endl;
cout << (*p)(a, 0) << endl;
return 0;
}
double* f4(double arr[], int n)
{
return arr + n;
}
输出结果:
这说明p(a, 0)等价于(*p)(a,0)
指针函数:int* fun(int x,int y);
指针函数本质是一个函数,其返回值为指针。
函数指针:int (*fun)(int x,int y);
函数指针本质是一个指针,其指向一个函数。
再次梳理
int func(int* a, int b)这是一个函数func,参数列表是(int* a,int b),返回值是int数据类型。那么,这个指向这个函数的函数指针可以写成int (*p)(int *a,int b)。调用这个函数指针可以写成(*p)(&a,b),此时返回的是int类型,按道理可以进一步写成int c = (*p)(&a,b)。
如果返回的不是一个int类型的数据,而是一个函数指针。假如一个函数是func,其参数列表为(int)类型,其返回值是函数指针int (p)(int a,int b)。
使用别名,将PF定义成指向函数类型的指针。
using PF = int (*)(int* a,int b);
那么func的声明就可以写成
PF func(int c);
func需要return一个函数指针,所以先定义这个函数指针。
int f1(int* a, int b)
{
std::cout << "a: " << &a << " b: " << b << std::endl;
return b;
}
int (*p)(int *a,int b) = f1; //定义函数指针指向函数f1
此时,就可以写出func的定义:
PF func(int c)
{
std::cout << "c:" << c << std::endl;
return *p; //返回函数指针
}
于是在main函数中可以写成:
int main()
{
int c = 10;
int d = 12
int e = 13;
func(c); //输出 c: 10 其实由于func返回的是一个函数指针,而函数指针可以直接(*p)(&a,b)来实现调用。
int (*p2)(int *,int) = func(c);
//下列两个等价
p2(&d,e);
func(c)(&d,e);
return 0;
}
数组指针
如果一个数组中的元素有几千个,则一个个的声明指针并赋值显得非常的麻烦和不方便
而数组指针是一种专门用于数组的一种指针。 指针指向一个数组的首地址。
定义数组指针变量:
语法:数据类型 (*数组指针变量名)[元素量];
将数组的地址储存到数组指针变量中:
语法:数组指针变量名 = &数组;
double a[3] = {
1.2, 1.3, 1.4};
double*p[3] = a; //error 错误 这是指针数组的声明方式
double (*p)[3] = a; //error 错误 a是数组的首个元素首地址
double (*p)[4] = &a; //error 错误 数组指针和数组的长度不相等
double(*p)[3] = &a; //正确 &a是数组首地址
数组指针的长度必须与指向的数组的长度保持一致。
指针数组
指针的数组可以理解为“指针的数组”,即数组中所有元素都是指针类型的。
语法:数据类型* 指针组名[元素量];
int var[3] = {
10,100,1000 };
const int* p[3];
for (int i = 0; i < 3; i++)
{
p[i] = &var[i];
}
for (int j = 0; j < 3; j++)
{
cout << p[j] << " " << *p[j] << endl;
}
const char* p2[3] = {
"hello",
"great",
"world"
};
for (int k = 0; k < 3; k++)
{
cout << p2[k] << " " << *p2[k] << endl;
}
char str0[10] = {
'Z','a','r','a',' ','A','l','i' };
cout << str0 << endl;
输出结果:
参考:C++指针数组
函数的形参用void*,表示接受任意数据类型的指针。
1.不能用void声明变量,它不能代表一个真实的变量;
2.不能对void* 指针直接解引用(需要转换成其他类型的指针);
3.把其他类型的指针赋值给void* 指针不需要转换;
4.把void*指针赋值给其他类型的指针需要转换。
指针类型转换例程
int a = 10;
int* p = &a;
float b = 1.1;
float* p2 = &b;
cout << "p: " << p << endl;
cout << "*p: " << *p << endl;
cout << "p2: " << p2 << endl;
cout << "*p2: " << *p2 << endl;
p = (int*)p2;
cout << "p: " << p << endl;
cout << "*p: " << *p << endl;
cout << "p2: " << p2 << endl;
cout << "*p2: " << *p2 << endl;
结果如下:
p和p2都指向了同一个地址,但是解引用出来的结果不一样。这篇博客给出了一些解释——C/C++强制类型转换、指针类型转换发生了什么。
指针越界
防止指针越界的方法
1.必须让指针指向一个有效的内存地址;
2.防止数组越界;
3.防止向一块内存中拷贝过多的内容;
4.防止使用空指针;
5.防止改变const修改的指针;
6.防止改变指向静态存储区的内容;
7.防止两次释放同一个指针;
8.防止使用野指针。
全局变量和局部变量在内存中的区别
全局变量储存在静态数据库,局部变量在堆栈。
Heap和stack的差别
Heap是堆,stack是栈;
stack的空间由操作系统自动分配/释放,heap上的空间手动分配/释放;
stack空间不足够大且有限,heap是很大的自由储存区;
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。