C语言指针、回调函数

内存

内存(Memory)是计算机的重要部件,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。

为了高效的使用内存,计算机会把整块内存分成一块块小的内存单元,每块内存单元1字节,并为每块内存单元编号,这些编号就被称为内存单元的地址。

变量的创建其实就是在内存中为其分配空间,每个内存单元都有地址,所有变量也是有地址的,下面通过代码来展示

int main()
{
	int a = 0;
	//通过&符号获取a的地址
	//int类型的大小为4个字节,使用此处打印出来的是首地址,也就是较小的地址
	//%p是用于打印地址
	printf("%p", &a);

	return 0;
}

运行结果

008FFBF0

指针的使用

一级指针的使用

指针会频繁使用到的操作

  1. 定义一个指针变量
  2. 把变量地址赋值给指针
  3. 访问指针变量中可用地址的值
int main()
{
	int a = 1;
	//定义一个指针变量
	//语法:数据类型 *标识符;
	int* p;

	//把变量地址赋值给指针
	//指针p的类型必须跟变量a的类型一样
	p = &a;

	//访问指针变量中可用地址的值
	//通过*来解引用p,获取p指向的地址的值
	int b = *p;
	printf("p = %p\nb = %d", p, b);

	return 0;
}

运行结果

p = 010FFCA8
b = 1

### 二级指针的使用

二级指针表示指向指针变量的指针

int main()
{
	int a = 1;
	//一级指针
	int* p = &a;
	//二级指针,*表示一级指针,**表示二级指针
	int** pp = &p;

	printf("a = %p\n", &a);
	printf("p = %p\n", p);
	printf("pp = %p\n", pp);
	printf("&pp = %p\n", &pp);

	return 0;
}

运行结果

a = 0022F894
p = 0022F894
pp = 0022F888
&pp = 0022F87C

NULL指针

NULL指针也叫空指针,在没有确切的地址赋予给指针变量时,我们可以把NULL值赋予给指针变量,这是一个良好的编程习惯。

代码示例

int main()
{
	int* p = NULL;
	printf("p的地址为:%p", p);
	return 0;
}

运行结果:

p的地址为:00000000

指针变量的大小

指针变量的大小其实就是地址的大小

地址的大小取决于计算机是32位平台还是64位平台

32位平台的计算机的地址为32bit,也就是4字节

64位平台的计算机的地址为64bit,也就是8字节

int main()
{
	printf("整数指针的大小:%d \n", sizeof(int*));
	printf("字符指针的大小:%d \n", sizeof(char*));
	printf("浮点指针的大小:%d \n", sizeof(double*));
	return 0;
}

运行结果

整数指针的大小:4
字符指针的大小:4
浮点指针的大小:4

指针的算术运算

指针的算术运算是把指针指向的位置向前或者先后移动,指针的算术运算符:++、–、+、-

假如对整数指针进行+n的操作,那么就是把整数指针指向下一个整数位置,即以整数类型4个字节大小向后移动

假如对字符指针进行-n的操作,那么就是把字符指针指向上一个字符位置,即以字符类型1个字节大小向前移动

代码示例

int main()
{
	int a = 0;
	int* p = &a;
	printf("p的地址为:%p\n", p);
	printf("p+2的地址为:%p\n", p+2);
	printf("p-2的地址为:%p\n", p-2);
	printf("++p的地址为:%p\n", ++p);
	printf("--p的地址为:%p\n", --p);

	return 0;
}

运行结果

p的地址为:00B8FA74
p+2的地址为:00B8FA7C
p-2的地址为:00B8FA6C
++p的地址为:00B8FA78
–p的地址为:00B8FA74

字符指针

字符指针的常见用法

int main()
{
	char a = 'a';
	char* p;
	p = &a;
	printf("%c", *p);
	return 0;
}

除了上面的用法,我们还可以把字符指针指向字符串

int main()
{
	//这里很容易被理解为把字符串Hello World放到指针pstr里面,
	//指针pstr里面存放的不是整个字符串,而是把字符串的首字符的地址存放到pstr里面
	//因为这里是指向字符串常量,所以需要用const修饰
	const char* pstr = "Hello World";
	printf("%s", pstr);

	char str[10] = "abcd";
	const char* pstr1 = str;
	return 0;
}

指针数组

指针数组:存放指针变量的数组

int main()
{
	//整数指针数组
	int* arr1[2];
	//字符指针数组
	char* arr2[2];

	int x = 1;
	int y = 2;
	int z = 3;
	int* px = &x;

	int* arr3[3] = {px,&y,&z};

	return 0;
}

数组指针

数组指针的定义

数组指针:指向数组的指针,数组指针的指针变量

代码示例

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//定义数组指针
	int(*p)[10];
	p = &arr;

	return 0;
}

我对int(*p)[10]的理解:

  1. 先找到变量的名字pp*()括起来优先级高,p*先结合,说明这是一个指针变量
  2. (*p)拎出来,剩下int [10],既然这是一个指针变量,那么把指针变量的名字p*找出来了,剩下的就是指针变量指向的类型
  3. int [10]里面,[]表示这是一个数组,里面的数字表示数组元素的个数,最后剩下的就是数组元素的类型,所以int [10]代表这是一个数组,数组元素的个数是10,数组元素的类型是int
  4. 所以int(*p)[10]表示是一个指针,指向一个数组,数组元素的个数是10,数组元素的类型是int

&数组名和数组名的区别

打印&数组名和数组名的地址

int main()
{
	int arr[4] = { 1,2,3,4 };
	printf("arr的地址:%p\n", arr);
	printf("&arr的地址:%p\n", &arr);

	return 0;
}

运行结果

arr的地址:0053FC2C
&arr的地址:0053FC2C

arr和&arr的地址一样,难道这两没有区别?

我们再看一段代码

int main()
{
	int arr[4] = { 1,2,3,4 };
	printf("arr的地址:%p\n", arr);
	printf("&arr的地址:%p\n", &arr);

	printf("arr+1的地址:%p\n", arr+1);
	printf("&arr+1的地址:%p\n", &arr+1);

	return 0;
}

运行结果:

arr的地址:008FFBD0
&arr的地址:008FFBD0
arr+1的地址:008FFBD4
&arr+1的地址:008FFBE0

总结:

arr

arr表示的是数组首元素的地址

arr的类型是int*,是整型指针

arr+1指向的是下一个整型的位置,大小为4个字节

&arr

&arr表示的是数组的地址,而不是数组首元素的地址

&arr的类型是int (*)[4],是数组指针

&arr+1指向的是下一个数组的位置,大小为数组大小,这里为16个字节

函数指针

函数指针表示指向函数的指针。

通过下面的示例拿到函数地址

int max(int x, int y) 
{
	return x > y ? x : y;
}

int main()
{
	printf("max = %p\n", max);
	printf("&max = %p\n", &max);
	return 0;
}

运行结果

max = 003813B1
&max = 003813B1

&函数名函数名

这里我们发现&函数名函数名都是函数的地址,那&函数名函数名的含义有区别吗?

其实&函数名函数名的含义是一样的,都代表函数地址。

我们知道了函数的地址,那应该放到哪里呢?

int max(int x, int y) 
{
	return x > y ? x : y;
}

int main()
{
	//定义一个函数指针
	int (*fun)(int, int);
	//给函数指针赋值
	fun = max;

	//使用函数指针指向的函数
	//方法一
	int res1 = fun(3, 5);
	//方法二
	int res2 = (*fun)(6, 8);
	printf("res1 = %d\n", res1);
	printf("res2 = %d\n", res2);

	
	return 0;
}

运行结果

res1 = 5
res2 = 8

我对int (*fun)(int, int)的理解:

  1. 先找到变量的名字funfun*()括起来优先级高,fun*先结合,说明这是一个指针变量
  2. (*fun)拎出来,还剩下int (int, int),既然这是一个指针变量,那么把指针变量的名字fun*找出来了,剩下的就是指针变量指向的类型
  3. int (int, int)里面,()如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (int, int)代表这是一个函数,这个函数有两个int类型形参和返回值类型为int
  4. 所以int (*fun)(int, int)表示是一个指针,指向一个函数,这个函数有两个int类型形参和返回值类型为int

函数指针数组

函数指针数组表示存放函数指针变量的数组

代码示例

int max(int x, int y) 
{
	return x > y ? x : y;
}

int add(int x, int y) 
{
	return x + y;
}

int main()
{
	//定义函数指针数组,并初始化
	int (*fun_arr[2])(int, int) = { max,add };

	//使用函数指针数组里面的函数指针指向的函数
	int res = (*fun_arr[1])(4, 5);
	printf("%d", res);

	return 0;
}

运行结果

9

我对int (*fun_arr[2])(int, int)的理解:

  1. 先找到变量的名字fun_arrfun_arr两边分别是*[2],因为[]的优先级高,所以先和[2]结合,发现这是一个数组
  2. fun_arr[2]拎出来,还剩下int (*)(int, int),既然这是一个数组,那么把数组的数组名和数组长度fun_arr[2]找出来了,剩下的就是数组里面元素的类型
  3. int (*)(int, int)里面,*代表这是一个指针,()如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (*)(int, int)代表这是一个指针,这个指针指向有两个int类型形参和返回值类型为int的函数
  4. 所以int (*fun_arr[2])(int, int)表示是一个数组,长度为2,存放的元素类型为指向有两个int类型形参和返回值类型为int的函数的指针

指向函数指针数组的指针

//指向函数指针数组的指针
int max(int x, int y)
{
	return x > y ? x : y;
}

int add(int x, int y)
{
	return x + y;
}

int main()
{
	//这是一个函数指针数组
	int (*fun_arr[2])(int, int) = { max,add };

	//定义一个指针指向上面的数组,这个指针就是函数指针数组指针
	int (*(*p_fun_arr)[2])(int, int);
	p_fun_arr = &fun_arr;

	//使用函数指针数组指针指向的数组里面的函数指针指向的函数
	int res = ((*p_fun_arr)[1])(3, 5);
	printf("%d", res);

	return 0;
}

运行结果

8

我对int (*(*p_fun_arr)[2])(int, int)的理解:

  1. 先找到变量的名字p_fun_arrp_fun_arr*()括起来优先级高,p_fun_arr*先结合,说明这是一个指针变量
  2. (*p_fun_arr)后面跟着[2],说明这是一个数组指针,指向的这个数组的元素个数为2
  3. (*p_fun_arr)[2]拎出来,还剩下int (*)(int, int),既然这是一个数组指针,那么剩下的就是指针指向的数组里面元素的类型
  4. int (*)(int, int)里面,*代表这是一个指针,()如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (*)(int, int)代表这是一个指针,这个指针指向有两个int类型形参和返回值类型为int的函数
  5. 所以int (*(*p_fun_arr)[2])(int, int)表示是一个指针,指针指向一个数组,数组的元素个数为2,数组元素的类型为指向有两个int类型形参和返回值类型为int的函数的指针

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

代码示例

int max(int x, int y)
{
	return x > y ? x : y;
}

int sum(int x, int y)
{
	return x + y;
}

int test(int x, int y, int (*pf)(int, int))
{
	return pf(x, y);
}

int main()
{
	//使用函数指针作为参数
	int res1 = test(3, 5, max);
	printf("res1 = %d\n", res1);
	int res2 = test(3, 5, sum);
	printf("res2 = %d\n", res2);
	return 0;
}

运行结果

res1 = 5
res2 = 8

本文出现任何错误,欢迎留言批评指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值