C语言指针、回调函数
内存
内存(Memory)是计算机的重要部件,也称内存储器和主存储器,它用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。它是外存与CPU进行沟通的桥梁,计算机中所有程序的运行都在内存中进行,内存性能的强弱影响计算机整体发挥的水平。只要计算机开始运行,操作系统就会把需要运算的数据从内存调到CPU中进行运算,当运算完成,CPU将结果传送出来。
为了高效的使用内存,计算机会把整块内存分成一块块小的内存单元,每块内存单元1字节,并为每块内存单元编号,这些编号就被称为内存单元的地址。
变量的创建其实就是在内存中为其分配空间,每个内存单元都有地址,所有变量也是有地址的,下面通过代码来展示
int main()
{
int a = 0;
//通过&符号获取a的地址
//int类型的大小为4个字节,使用此处打印出来的是首地址,也就是较小的地址
//%p是用于打印地址
printf("%p", &a);
return 0;
}
运行结果
008FFBF0
指针的使用
一级指针的使用
指针会频繁使用到的操作
- 定义一个指针变量
- 把变量地址赋值给指针
- 访问指针变量中可用地址的值
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]
的理解:
- 先找到变量的名字
p
,p
和*
被()
括起来优先级高,p
和*
先结合,说明这是一个指针变量- 把
(*p)
拎出来,剩下int [10]
,既然这是一个指针变量,那么把指针变量的名字p
和*
找出来了,剩下的就是指针变量指向的类型int [10]
里面,[]
表示这是一个数组,里面的数字表示数组元素的个数,最后剩下的就是数组元素的类型,所以int [10]
代表这是一个数组,数组元素的个数是10,数组元素的类型是int
- 所以
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)
的理解:
- 先找到变量的名字
fun
,fun
和*
被()
括起来优先级高,fun
和*
先结合,说明这是一个指针变量- 把
(*fun)
拎出来,还剩下int (int, int)
,既然这是一个指针变量,那么把指针变量的名字fun
和*
找出来了,剩下的就是指针变量指向的类型int (int, int)
里面,()
如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (int, int)
代表这是一个函数,这个函数有两个int
类型形参和返回值类型为int
- 所以
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)
的理解:
- 先找到变量的名字
fun_arr
,fun_arr
两边分别是*
和[2]
,因为[]
的优先级高,所以先和[2]
结合,发现这是一个数组- 把
fun_arr[2]
拎出来,还剩下int (*)(int, int)
,既然这是一个数组,那么把数组的数组名和数组长度fun_arr[2]
找出来了,剩下的就是数组里面元素的类型int (*)(int, int)
里面,*
代表这是一个指针,()
如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (*)(int, int)
代表这是一个指针,这个指针指向有两个int
类型形参和返回值类型为int
的函数- 所以
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)
的理解:
- 先找到变量的名字
p_fun_arr
,p_fun_arr
和*
被()
括起来优先级高,p_fun_arr
和*
先结合,说明这是一个指针变量(*p_fun_arr)
后面跟着[2]
,说明这是一个数组指针,指向的这个数组的元素个数为2- 把
(*p_fun_arr)[2]
拎出来,还剩下int (*)(int, int)
,既然这是一个数组指针,那么剩下的就是指针指向的数组里面元素的类型int (*)(int, int)
里面,*
代表这是一个指针,()
如果包括着无、一个或多个数据类型,代表这是一个函数并且这个函数的形参的类型是什么,最后剩下的是这个函数的返回值类型,所以int (*)(int, int)
代表这是一个指针,这个指针指向有两个int
类型形参和返回值类型为int
的函数- 所以
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
本文出现任何错误,欢迎留言批评指正。