C语言学习——一阶二阶指针传参、函数指针的介绍及其应用、回调函数

一级指针二级指针的传参

一级指针传参

若自定义函数的形参为一级指针变量,在调用此函数时要如何传递参数?

  • 接收是指针变量,则传递也可以是指针变量。
  • 接收的是指针变量,则传递可以是地址。
void test1(int* p)    
{
   }
//若接收为指针变量,可以传递什么样的参数
void test2(char* str)
{
   }
int main()
{
   
	int arr[5] = {
    1, 2, 3, 4, 5 };
	int* p = arr;
	test1(p);      //使用指针变量传参
	test1(arr);    //使用数组名传参
	char b = 'w';
	test2(&b);     //对变量进行取地址后传参
	char* p1 = &b;
	test2(p1);
	return 0;
}

在这里插入图片描述

多数情况下数组名表示首元素地址(如上图,右面是整型数组,左面是数组中每个元素的地址),因此可以直接作为参数进行传递;可以对变量进行取地址之后(&变量名)进行传递;还可以直接传递指针变量。

数组名代表首元素地址的两种情况:

  • sizeof(数组名)——此时,数组名表示整个数组。
  • &数组名——表示取整个数组的地址。

二级指针传参

自定义函数的形参是二级指针调用时如何传递参数呢?

  • 可以通过二级指针,进行传参。
  • 使用一级指针的地址进行传参。
  • 指针数组的数组名可以直接用来传参。
void test(int** p)
{
   }
int main()
{
   
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;  //不能这样写&(&a)
	//可以传二级指针
	test(ppa);
	//可以传一级指针的地址
	test(&pa);
	int* b[5] = {
    0 };
	//数组名表示首元素地址,因此可以传递指针数组名
	test(b);
	return 0;
}

如上面代码展示的,二级指针不能写为:int** ppa = &(&a);为什么呢?请看下图:
变量a
上图是对变量a的地址及其存储内容。
指针变量pa
上图是一级指针变量pa的地址及其存储内容。
二级指针变量ppa
上图是二级指针变量ppa的地址及其存储内容。三者的关系如下图:
变量和一级指针和二级指针变量三者关系
一级指针pa中存放的是变量a的地址,二级指针内部存放的是一级指针的地址,并不是一级指针内部存放的内容。而&a拿到了a的地址,但并没有为它开辟内存空间,因此无法进行二级指针操作。

指针数组中的元素是指针,数组名还表示首元素地址,因此指针数组的数组名可以进行传参。举个栗子:

void test(int** p)
{
   }
int main()
{
   
	int b = 4;
	int c = 3;
	int d = 2;
	int* e[3] = {
    &b, &c, &d };
	test(e);
	return 0;
}

变量b、c、d的地址分别为:

指针数组e的地址及其存储内容为:

指针数组e
数组e中存放变量b、c、d的地址,数组名表示首元素地址0x004FF7DC类似于一级指针其可以作为参数进行传递。

函数指针

函数指针

函数名就是地址。函数指针——指向函数的指针。函数指针的创建:指向函数的返回类型 (*函数指针变量)(指向函数的形参类型) = &指向函数(不用&也可以);,以一个简单的例子对函数指针的创建进行简要描述:

int Add(int x, int y)
{
   
	return x + y;
}
int main()
{
   
	//函数指针的创建,首先其为指针要表示为*pf,(*pf)()表示其指向函数,
	//(int, int)是其指向函数的形参类型,int表示返回类型
	int (*pf)(int, int) = &Add;
	//传参,首先将函数指针解引用,再传参数,这颗*没有意义,只是为了方便理解
	int ret = (*pf)(3, 5);
	//也可以这样, Add==pf
	int num = pf(3, 5);
	printf("%d\n", num);
	printf("%d\n", ret);
	//还需要注意,函数指针的&Add和Add的具有同样的意义。
	printf("%p\n",&Add);
	printf("%p\n",Add);
	return 0;
}

代码中Add和等价于pf,可以直接通过pf进行传参。代码的输出结果为:
函数指针输出结果
&AddAdd输出的地址一致,两者意义相同,注意区分数组的地址&arr和数组名arr,两者输出的地址也一样,&arr表示整个数组的地址,arr表示数组首元素的地址。

int main()
{
    
	int arr[5] = {
    1, 2, 3, 4, 5};
	printf("%p\n", &arr);
	printf("%p\n", arr);
	printf("%p\n", &arr+1);
	printf("%p\n", arr+1);
	return 0;
}

在这里插入图片描述
&arr是整个数组的地址,但其和arr都指向首元素地址,不同在于将其加1之后跳过的是整个数组(此例中数组大小为 5 × 4 = 20 5\times4=20 5×4=20个字节),地址从0x00D3F8E00x00D3F8F0,注意&arr+n跳过的是n个数组大小,此时以数组为单位,而arr+1表示从首元素地址跳至第二个元素地址。在这里插入图片描述

尝试理解下面两行代码以加深对函数指针的理解(这里内容很多而且难理解,一定要好好看完):

(*(void (*)())0)();
// 第一步:void (*)()表示函数指针。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值