十.C语言深入理解指针(3)

前言:  希望大家对于上篇的知识有所吸收,接下来让我们更深入了解指针吧!

   (一定要掌握好指针哦!很重要)

1.字符指针变量

1.1 语法

我们学了整型指针int *   当然也有 字符指针 char *  

语法规则是通用的

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

还有一种使用方法,但这个方法稍微复杂

int main()
{
	const char* pstr = "hello bit. ";
	//本质并不是把字符串‘hello bit.’放到指针
	//而是把字符串‘hello bit.’首字符地址放到了pstr 中
	printf("%s\n", pstr);

	return 0;
}

仔细观察,其实跟数组指针差不多的,把字符串/数组  首个字符/元素放在指针变量中

1.2 深入内存

下面是《剑指offer》中收录的一道题

int main()
{
 char str1[] = "hello bit.";
 char str2[] = "hello bit.";
 const char *str3 = "hello bit.";
 const char *str4 = "hello bit.";
 if(str1 ==str2)
 printf("str1 and str2 are same\n");
 else
 printf("str1 and str2 are not same\n");
 
 if(str3 ==str4)
 printf("str3 and str4 are same\n");
 else
 printf("str3 and str4 are not same\n");
 
 return 0;
}

当然,答案是   

str1 and str2 are not same 

str3 and str4 are same

解析:

c/c++  中

1. 在用相同常量字符串去初始化不同数组的时候会开辟出不同的内存块

2.当几个指针指向同一个字符串的时候,它们实际指向同一块内存

2.数组指针变量

2.1 数组指针定义

在上一篇文章中,其实我们已经感受到了数组指针跟指针数组不是一回事  

指针数组是一种数组,数组中存放的是地址(指针)

而数组指针是一种指针变量,全称:数组指针变量

整型指针变量:int * pint; 存放的是整型变量的地址,指向整形数据的指针

浮点型指针变量:float * pf; 存放的是浮点型变量的地址,指向浮点型数据的指针

数组指针:int  (*p) [ 10 ];    存放的是数组变量的地址,指向数组数据的指针

2.2 数组指针语法

有人会问 这样写对不对呢

  int *p [10];  

首先   

  int *p [10];  

  int  (*p) [ 10 ]; 

这两种写法表示的意思完全不同

int *p[10]

是一个指针数组,这意味着p 是一个有 5个元素的数组,每个元素都是指向 int 类型的指针(可以把 p[10] 看成一个整体  这就是一个整型指针)

int (*p)[10]

是数组指针,这意味它指向一个具有 5个int 类型元素的数组

原因:

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

2.3数组指针初始化

如何得到数组的地址呢?

用我们之前学过的 &数组名

int  arr [ 10 ] = { 0 };

&arr;  //得到的就是数组的地址 

当然要存放一个数组的地址,就得存放在指针数组变量

int (*p) [ 10 ] = &arr ;

可以看到 &arr 和 p 的类型是完全一致的

3.二维数组传参的本质

过去我们将一个二维数组传参给一个函数的时候,是这样写的:

void test(int a[3][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", a[i][j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	test(arr, 3, 5);
	return 0;
}
首先我们再次理解⼀下二维数组,二维数组起始可以看做是每个元素是⼀维数组的数组,也
就是二维 数组的每个元素是⼀个一维数组。那么二维数组的首元素就是第一行,是个⼀维数
组。
所以,根据数组名是数组首元素的地址这个规则,二维数组的数组名表⽰的就是第一行的地
址,是⼀ 维数组的地址。根据上面的例子,第一行的⼀维数组的类型就是 int [5] ,所以第⼀
行的地址的类 型就是数组指针类型 int(*)[5]
也就是说,二维数组传参的本质也是传递了地址,传递的是第一行这个一维数组的地址,所以形参也是可以写成指针形式的

4.函数指针变量

4.1函数指针变量定义

通过前面的 学习,我们可以类比出结论

函数指针变量是存放函数地址的,未来通过地址能够调用函数

我们来做个测试,证明函数也有地址

当然,也可以通过   &函数名  的方式获得函数地址

函数指针变量的写法其实和数组指针非常相似,如下

函数指针类型解析

4.2 函数指针变量的使用

上图

5. typedef 关键字

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

比如

如果你觉得 unsigend int  写起来不方便,如果能写成 uint 就方便多了,那我们就可以使用

当然指针类型也可以重命名     int* → ptr_t

但对于数组指针和函数指针稍微有些区别

比如我们有数组指针类型 int(*)[5] ,需要重命名为 parr_t 那可以这样写:
函数指针也一样 ⽐如,将 void(*)(int) 类型重命名为 pf_t ,就可以这样写:

6.函数指针数组

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

int  (*  parr1  [ 3 ]  ) ();

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

7.转移表

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

举例:计算器的一般实现

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(" 0:exit \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;
}

使用函数指针数组实现:

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 }; //转移表
	//想使用哪个函数,利用指针定位
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf(" 0:exit \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("输⼊操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
			printf("ret = %d\n", ret);
		}
		else if (input == 0)
		{
			printf("退出计算器\n");
		}
		else
		{
			printf("输⼊有误\n");
		}
	} while (input);
	return  0;
}

不难看出,转移表省了老多事,其实这就是指针的优势性

总结:指针大概内容就是这几篇文章了,当然对于 qsort  回调函数 等一些内容我还是会讲的,但由于作者学业紧张,这些内容我会在不影响大家学习的情况下抽空讲,希望大家谅解,也感谢大家观看!

作者留言:本人初学者,如有错误和不恰当的地方,欢迎留言!

                         创作时间:2024.1.10

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值