学习C语言(十二)——深入理解指针(3)

目录

1.字符指针变量

2.数组指针变量

2.1 什么是数组指针变量?

2.2 数组指针变量初始化

3.二维数组传参的本质

4.函数指针变量

4.1 函数指针变量的创建

4.2 函数指针的使用

 4.3 两段有趣的代码

4.3.1 typedef关键字

5.函数指针数组

6.转移表


1.字符指针变量

        在指针的类型中我们知道有一种指针类型为字符指针 char* 。示例如下:

         还有一种使用方法如下:

         代码 char* pstr = "ABCDEFG" 容易让人以为是吧字符 ABCDEF 放到了字符只恨 pstr中,但是本质上是把字符串的首字符放到了pstr中。

2.数组指针变量

2.1 什么是数组指针变量?

        在之前的学习中我们知道,指针数组是一种数组,数组中存放的是地址(指针)。

        根据之前的指针变量:

  • 整形指针变量:int* pi;存放的是整型变量的地址,能够指向整型数据的指针。
  • 字符指针变量:char* pc;存放字符变量的地址,能够指向字符型数据的指针。

        那么,数组指针变量就是:存放数组的地址,能够指向数组的指针变量。

        分辨指针数组和数组指针变量

int* p1[10];	//指针数组
int(*p2)[10];	//数组指针变量

         解析:数组指针变量:p 先和 * 结合,说明 p 是一个指针变量,然后指向的是一个大小为10个正行的数组,所以 p 是一个指针,指向一个数组,叫数组指针

        注意:[] 的优先级要高于 * 号的,所以必须加上 () 来保证 p 先和 * 结合。

2.2 数组指针变量初始化

        数组指针是用来存放数组的地址,如果要存放整个数组的地址,就得存放在数组指针变量中。示例如下:

         解析:int 是 p 指向的数组元素类型;(*p) 的 p 是数组指针变量名,[5] 是p指向数组的个数;&arr 是得到数组的地址。

3.二维数组传参的本质

        在之前,我们有一个二维数组需要传参给一个函数时,我们是这样写的。代码如下:

         这里的实参是二位数组,形参也可以写成二位数组的形式,那还有什么写法呢?

         首先来再次理解一下二维数组,二位数组其实可以看做是每个元素的一维数组的数组,也就是二位数组的每个元素是一个一维数组。那么二维数组的首元素(也就是第一行),是一个一维数组、

        那么,根据数组名是数组首元素的地址这个规则,二维数组的数组名表示的就是第一行的地址,是一维数组的地址。。再根据上面的二维数组给函数传参的例子,第一行的一维数组的类型及时 int[4] ,所以第一行的地址的类型就是数组指针类型 int(*)[5]。那就意味着二维数组传参本质上也是传递了地址,传递的是第一行这个一维数组的地址。

        总结:二维数组传参,传参的部分可以写成数组,也可以写成指针形式。

4.函数指针变量

4.1 函数指针变量的创建

        在之前的学习中我们知道,数组指针是用来存放数组的地址(指针)。

        所以,函数指针变量,是用来存放函数的地址(指针)。

        测试一下怎么打印出函数的地址。示例如下:

         能够打印出函数的地址,所以函数时有地址的,函数名就是函数的地址。当然也可以通过 &函数名 的方式获得函数的地址。

        如果我们要讲函数的地址存放起来,就需要创建函数指针变量,函数指针变量的写法其实和数组指针的写法非常类似。示例如下:

         解析:以 int (*pf3) (int x,int y) 为例,int pf3指向函数的返回类型,(*pf3)是函数指针变量名,(int x,int y)是pf3指向函数的参数类型和个数的交代。int (*) (int x,int y)是pf3函数指针变量的类型。

4.2 函数指针的使用

 4.3 两段有趣的代码

        下面来介绍两段代码,均出自《C陷阱和缺陷》这本书。

        代码一:

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

        在第一个括号内的是一个类型转换表达式,即 *(void (*)())0 ,表示的是将整数 0 强制转换为函数指针类型。

        * 是对函数指针的一个解引用,获取指针指向的函数

        最后的 () 是对函数进行调用。

        代码二:

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

         在第一个括号内的是一个函数指针,signal 是函数名。

        int是函数中的第一个参数的类型;void(*)(int)是函数第二个参数的类型,是一个函数指针,并且该函数指针的返回类型是 void。

        刚开始的 void 是signal函数的返回类型,表示该函数不返回值。

        最外层的 void(*)(int) 是一个函数指针,指向一个接受整数参数并且返回类型为void的函数。

4.3.1 typedef关键字

        typedef 是用来类型重命名的,可以将复杂的类型简单化。

        例如,想要写一个 unsigned int 类型,但是写起来不方便,就可以使用typedef定义成 unint ,这样就会方便一点。

         那么,如何将这种方法应用到指针类型上呢?示例如下:

typedef int* ptr_t;
//将 int* 重命名为 ptr_t

         对于数组指针和函数指针,会有一点区别:

        比如,数组指针 int (*)[5],将其重命名为parr_t,示例如下:

typedef int(*parr_t)[5];
//新的类型名必须写在*号右边

         函数指针类型的重命名也要将新的类型名写在*号右边。比如:void (*)(int) 类型重命名为 pf_t ,示例如下:

typedef int(*pf_t)(int);
//新的类型名必须写在*号右边

         那么对之前的两个函数指针类型嵌套使用的情况,我们就可以使用typedef来简化代码。首先我们要知道,代码二整体的函数指针类型是 void (*)(int),其次,signal函数中的第二个参数类型也是 void (*)(int) ,那么就可以重新命名这个类型名,然后重复使用。示例如下:

void (*signal(int, void(*)(int)))(int);	//简化之前
typedef void (*pfu_t)(int);				//将 void (*)(int) 类型名重命名为 pfu_t
pfu_t signal(int, pfu_t);				//简化之后

5.函数指针数组

        整数指针数组:是数组,数组中存放的都是整数指针。

        根据这个定义,那么可以推断,把函数的地址存放到一个数组中,那么这个数组就叫函数指针数组。

         pArr先和 [] 结合,说明pArr是一个数组,数组的内容是 int(*)() 类型的函数指针。

6.转移表

        函数指针数字的用途:转移表。

        比如:计算机的一般实现。当我们使用以前的知识写代码,可能会使用 do......while 循环,switch语句,以及一些函数来组成这个代码。示例如下:

int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
void menu()
{
	printf("*******************************\n");
	printf("******* 1.add   2.sub *********\n");
	printf("******* 3.mul   4.div *********\n");
	printf("*********** 0.exit ************\n");
	printf("*******************************\n");
}
int main()
{
	int x = 0;
	int y = 0;
	int ret = 0;
	int input = 0;
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

        switch 语句中的每个case部分都有大量的重复,我们也可以将其写成函数,形参就用函数指针,这样可以精简一部分代码。示例如下:

int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
void menu()
{
	printf("*******************************\n");
	printf("******* 1.add   2.sub *********\n");
	printf("******* 3.mul   4.div *********\n");
	printf("*********** 0.exit ************\n");
	printf("*******************************\n");
}
void calc(int (*pf)(int ,int ))
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:");
	scanf("%d %d", &x, &y);
	ret = pf(x, y);
	printf("%d\n", ret);
}
int main()
{
	int x = 0;
	int y = 0;
	int ret = 0;
	int input = 0;
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			calc(add);
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

        但是当出现新的功能(如:%、>>、<<等)时,除了必要的写新的函数之外,还要增加switch语句中的case部分,最后主函数的代码会越来越长。

        我们可以使用函数指针数组来试一试。代码如下:

int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
void menu()
{
	printf("*******************************\n");
	printf("******* 1.add   2.sub *********\n");
	printf("******* 3.mul   4.div *********\n");
	printf("*********** 0.exit ************\n");
	printf("*******************************\n");
}
int main()
{
	int x = 0;
	int y = 0;
	int input = 1;
	int (*pf[10])(int, int) = { 0,add,sub,mul,div };
	do
	{
		menu();
		printf("请输入你的选择:");
		scanf("%d",&input);
		if (input > 0 && input <= 4)
		{
			printf("请输入两个操作数:");
			scanf("%d %d", &x, &y);
			int ret = pf[input](x, y);
			printf("%d\n", ret);
		}
		else if (input == 0)
			printf("退出计算器\n");
		else
			printf("输入错误,请重新输入\n");
	} while (input);
	return 0;
}

        这样在扩展代码时,代码就不会继续变长。只需要添加新功能的函数指针到数组中,并更改if判断条件中的 input<=4 中的数字即可,代码容易更改,也不会变得繁长。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值