十二.指针详解(2)

4.数组参数、指针参数

一维数组传参

#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int* arr)
{}
void test2(int* arr[20])
{
void test2(int** arr)
{}
//这些接受实参的方式都是可以的
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

二维数组传参

void test(int arr[3][5])
{}
//void test(int arr[][])//这是错误的写法
//{}
void test(int arr[][5])
{}
void test(int* arr)
{}
void test(int* arr[5])
{}
void test(int(*arr)[5])
//指向数组的指针来接受一个二维数组也行
//但是要注意这个指针会指向数组中的某一行,刚开始可能是第一行
{}
void test(int** arr)
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
}
总结:二维数组传参,函数形参的设计只能省略第一个[]的数字,也就是行。因为对一个二维数组,可以不知道有多少行,但是必须知道一行有多少个元素,即需要知道有多少列,这样才方便运算
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

一级指针传参

#include <stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	//一级指针p,传给函数
	print(p, sz);
	return 0;
}

运行结果:1 2 3 4 5 6 7 8 9 0

那么反过来,当一个函数的形参是一个一级指针是,实参可以是什么呢

void test1(int *p)
{}
//test1函数能接收什么参数

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

二级指针传参

#include <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	int* arr[10] = { p };
	test(pp);
	test(&p);
	test(arr);
	return 0;
}

运行结果:

num = 10
num = 10
num = 10
 

总结:二级指针的形参可以传一级指针的地址、二级指针变量,甚至可以是一个指针数组

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

5.函数指针

由前面学习的数组指针、指针数组,我们应该可以推测出函数指针的作用。是的,函数指针就是存放函数地址的指针

函数的地址

我们先来看看函数地址是什么再来讨论怎么把它存起来

#include <stdio.h>
void test()
{
	printf("hehe\n");
}
int main()
{
	printf("%p\n", test);
	printf("%p\n", &test);
	return 0;
}

运行结果:

00007FF7F09713CF
00007FF7F09713CF

可见,函数确实是有一个地址的,但是两次打印的结果是一样的,难道说&函数名是函数的地址,函数名是首元素地址吗?当然不可能,函数哪来的什么首元素。所以,&函数名和函数名都是函数的地址,这两种方式拿到的都是函数的地址

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

函数指针

那我们应该怎么去把函数的地址给保存起来呢?回想一下之前学的数组指针,我们是这样去构建的

对于一个整型数组arr:int arr[10]。我们要写出对应的数组指针

第一步,它要是一个指针,所以有*p

第二步,考虑到*优先级低于 [ ],所以要用小括号括起来,让*先与p结合得到(*p)

第三步,用[ ]表示这个指针指向的是数组,数组有几个元素呢?10个,所以得到(*p)[10];第四步,每个元素是什么类型的呢,是int类型,所以最终得到int (*p)[10] = &arr

同样,对于一个加法函数Add,我们想要把它的地址存起来,就需要一个函数指针

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

第一步,我们需要一个指针,所以有*p

第二步,指针p指向的是一个函数,我们这样来表示*p(int,int),用一个小括号,里面放参数,用于表示这个指针函数有哪些参数(参数里面可以写变量即*p(int x,int y),也可以不写,就写一个类型*p(int,int))

第三步,函数的返回类型是int,所以得到 int *p(int,int)

第四步,由于这样写就不是一个指针,因为p先和小括号结合,然后int与*结合,最后变成了“p这个函数有两个int类型的参数,返回类型是int*”,这当然不是我们想要的结果。所以还是要给*p加上小括号,最终得到int (*p)(int,int) = Add

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

函数指针的使用

我们来验证一下,这个p里面到底是不是函数的地址。是不是函数地址我们用一下不就知道了。

首先我们拿到p,这里面放到是函数的地址,那我们对它解引用一下(*p)不就找到这个函数了吗,然后对着这个传参看看到底能不能使用

#include <stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 3;
	int b = 5;
	int (*p)(int, int) = Add;
	printf("直接调用函数Add得到:%d\n", Add(a, b));
	printf("使用函数指针p得到:%d\n",(*p)(a,b));
	return 0;
}

运行结果:

直接调用函数Add得到:8
使用函数指针p得到:8

可见,这个指针p里面确实可以存放一个函数的地址


void (*p) (int) = Add;//这是函数指针

void (*) (int) ———— 这是函数指针类型

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

尝试解释一下面两段代码

代码一

(*(void(*) () )0 )  ();

1)void(*) ():这是函数指针类型

2)(void(*) ()) 0:这是把0强制类型转换成void(*)()函数指针类型,也就是0变成地址了

3)(*(void(*) () )0 ):对地址解引用再加一个函数调用操作符(即小括号 “()” ),表示我要调用地址为0处的函数了

注:

1. 函数去掉函数名和参数就是返回类型

2. 数组去掉数组名和元素个数+方括号( [ ] )剩下的就是数组的元素类型

-- -- -- -- -- -- -- -- -- --

代码二

void (*signal(int , void(*)(int)))(int);

1)signal是一个函数声明

2)signal函数的参数有两个,int 和 void(*)(int),后者是一个函数指针,该函数指针指向的函数的参数是int,返回类型是void

3)signal函数的返回类型是void(*)(int),也是一个函数指针类型

也可以对代码二进行简化

typedef void (*pfun_t)(int);
//对void(*)(int) 这个函数指针类型重命名时,要把pfun_t写在*后面
//然后代码二就可以简化成下面的形式

pfun_t signal(int,pfun_t);
//但是不能像对类型重命名那样,写成 typedef unsigned int uint 这样的形式

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

对函数指针解引用真的有用吗

先看一段代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pa)(int, int) = &add;//add也行,前面讲过这都是函数的地址
	printf("%d\n", pa(2, 3));
	printf("%d\n", (*pa)(2, 3));
	printf("%d\n", (**pa)(2, 3));
	printf("%d\n", (********************pa)(2, 3));
	return 0;
}

运行结果:

5
5
5
5

由此可见,其实这个*根本没用啊,所以以后可以根据自习的习惯喜好来添加。当然不要像最后一行那样加一大堆 *,搞的别人以为你有什么特殊意思想要表达呢~~

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

 6.函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经介绍了指针数组,比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组。那函数指针的数组如何定义呢?

int (*parr1[10])();

int *parr2[10]();

int (*)() parr3[10];

答案是:int (*parr1[10])();

parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢? 是 int (*)() 类型的函数指针

-- -- -- -- -- -- -- -- -- -- -- -- -- -- --

函数指针数组的用途:转移表

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1.add *********** 2.sub \n");
		printf(" 3.mul *********** 4.div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("退出程序\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

	return 0;
}

使用函数指针数组可以省去一些case语句

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add           2:sub \n");
		printf(" 3:mul           4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);//调用函数
		}
		else
			printf("输入有误\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

7.指向函数指针数组的指针

“指向函数指针数组的指针”是一个指针,指针指向一个数组,数组中的元素都是函数指针

定义方式

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int test(int x, int y)
{
	return x + y;
}
int main()
{
	int arr[10] = { 0 };//一个数组
	int(*p)[10] = &arr;//数组指针
	int(*pf)(int, int);//函数指针
	int (*pfArr[4])(int, int) = { &test };//pfArr是函数指针数组
	int (*(*ppfArr)[4])(int, int) = &pfArr;//ppfArr是一个指向函数指针数组的指针
	//ppdArr是一个指针,指针指向的数组有四个元素
	//指向的数组中每个元素的类型是:int(*) (int,int),即函数指针
	return 0;
}

①此时实参可以是地址,也可以是存放了地址的一级指针变量

void test1(int* p)
{}
int main()
{
	int i = 10;
	int* p = &i;
	test1(&i);//ok
	test1(p);//ok
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

世长老

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值