C语言指针详解(进阶)

现在我们已经学习到了指针的进阶,这也可以分为两部分,主要的难点在于qsort的模拟实现。

  1. 进阶指针1(字符指针,数组指针,二维数组传参,函数指针变量(数组))
  2. 进阶指针2(回调函数,qsort使用,qsort的模拟实现)

进阶指针 1

字符指针变量:

字符变量的一般使用,但是这里需要注意,如果加入一个字符串,那么指针指向的是字符串第一个字符的地址。

数组指针变量: 

之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)

那么数组指针是什么呢?

是指向一个数组的指针变量。

第一行就是指针数组里面存放的指针。

第二行就数组指针是指向一个数组为10个元素的指针变量。 

解释:p先和*结合,说明p是⼀个指针变量变量,然后指着指向的是⼀个⼤⼩为10个整型的数组。所以p是⼀个指针,指向⼀个数组,叫数组指针。

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

 数组指针的初始化

数组指针是用来存放数组的地址的,我们可以存放地址数组的整个地址,在调试窗口下类型也是完全一致的。

 

 二维指针传参的本质:

平时我们二维数组传参:

 

 ⼆维数组传参本质上也是传递了地址,传递的是第⼀⾏这个⼀维数组的地址,那么形参也是可以写成指针形式的。如下:

 函数指针变量:

函数指针就是用来存放函数的地址的。

具体写法如下:

 函数指针解析:

 函数指针使用:

 typedef关键字:

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

比如:unsigned int   可以改成uint 

数组指针跟函数指针有点区别

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

 

 函数指针:

函数指针数组: 

我们已经学习了函数指针,那么函数指针数组就是在定义函数名的后面加上【】下标操作符就行了。

 转移表:

转移表就是通过一个函数指针数组来储存自己自定义的函数,在需要调用函数时,就可以通过函数指针数组的下标来访问。

下面我们用转移表来实现一般的计算机:

int jf(int x, int y)
{
	return x + y;
}
int jif(int x, int y)
{
	return x - y;
}
int cf(int x, int y)
{
	return x * y;
}
int cuf(int x, int y)
{
	return x / y;
}
void cd()
{
	printf("******************\n");
	printf("**1:加法  2:减法**\n");
	printf("**3:乘法  4:除法**\n");
	printf("**0:退出**********\n");
	printf("******************\n");

}
int main()
{
	int x, y;
	int a = 1;
	int ret = 0;
	do
	{
		cd();
		int (*p[5])(int x, int y) = { 0,jf,jif,cf,cuf };
		printf("请输入:");
		scanf("%d", &a);
		if (a >= 1 && a <= 4)
		{
			printf("请输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (p[a])(x, y);
			printf("%d\n", ret);
		}
		else if (a == 0)
		{
			printf("退出游戏\n");
		}
		else
		{
			printf("输入错误请重新输入\n");
		}

	} while (a);
}

 

 进阶指针2

回调函数是什么?:

回调函数就是⼀个通过函数指针调⽤的函数。

我们上面所讲到的转移表其实就是用了回调函数功能,创建一个函数指针数组,然后通过我们输入的下标来访问函数。

当然函数指针也是可以做到上面的转移表的,只不过比函数指针数组麻烦了不少。

qsort的使用举例:

需要包含头文件<stdlib.h>

 这是我在c++参考网站上找的关于qsort需要的参数,从上面可以看出qsort是用来排序升序数组的,算法都已经在内部帮我们写好了,所以用起来也非常简单。

以下是它需要的具体参数资料:

 具体实现:

int  px(const void *x, const void *y)
{
	return *(int*)x - *(int*)y;
}
//判断大小,返回大于0就交换,等于小于不交换。
int main()
{
	int a[] = {5,48,48,6,4,78,9,14,3,4,58,6,4,1,0,0 };
	int si = sizeof(a) / sizeof(a[0]);
	for (int c = 0; c < si; c++)
	{
		printf("%d ", a[c]);
	}
	printf("\n");
	qsort(a, si, sizeof(a[0]), px);
	for (int c = 0; c < si; c++)
	{
		printf("%d ", a[c]);
	}
	printf("\n");
	return 0;
}

这里 注意如果需要排序字符串需要用到头文件#include <string.h>,strcmp来比较:

int  name(const void* x, const void* y)
{
	return strcmp((*(gaa*)x).name, (*(gaa*)y).name);
}

模仿qsort的功能实现一个通用的冒泡排序:

//结构体
struct gaa
{
	char name[10];
	int age;
};
//比较大小并返回
int  sx(const void *x, const void *y)
{
	return strcmp(((struct gaa*)x)->name, ((struct gaa*)y)->name);
}
//交换
void jh(char* x, char* y,size_t si)
{
	for (int a = 0; a < si; a++)
	{
		char c = *x;
		*x= *y;
		*y = c;
		x++;
		y++;
	}
}
//循环判断
void qs(void* a, size_t se, size_t si,int (*sx)(void*x,void*y))
{
	for (int c = 0; c < se-1;c++)//第一趟
	{
		for (int d = 0; d < se - 1 - c; d++)//每一趟判断出一个数
		{
			if (sx((char*)a + d * si , (char*)a + (d + 1) * si) > 0)
			{
				jh((char*)a + d * si, (char*)a + (d + 1) * si, si);
			}

		}
	}

}
//打印
void acc( struct gaa a[], int se)
{
	for (int c = 0; c < se; c++)
	{
		printf("%s ", a[c].name);
		printf("%d \n",a[c].age);
	}
	printf("\n");
}

int main()
{
	//int  a[] = { 1,5,4,8,9,6,3,4,1 };
	//char  a[] = {"cbafhesi"};
	//float a[] = { 1.9,2.9,6.4,7.8,9.0 };
	struct gaa a[] = {{"zhangsan",20},{"lisi",19},{"wangwu",22} };
	int se = sizeof(a) / sizeof(a[0]);
	qs(a, se, sizeof(a[0]), sx);
	acc(a, se);
	return 0;
}

关于这里的强制转换成char*解释:

 这里强制转换成char类型是方便我们后期用字符类型进行排序,d*si是跳过一个4个字节进行比较,后面的d+1就是比前面多跳4个字节。

比如int类型前面d=0,si肯定等于4   ,a+0*4就是0,传过去的就是a第一个数据

那么第二个就是d+1=1,a+1*4(char等于一个字节,加4个字节),就是a第二个数据

 关于交换传每个数的大小解释:

这里还是如上是int类型的,假如条件成立进入函数,我们传给了jh一个char类型的函数所以,也得用char类型的来接收,而因为char类型是一个字节,所以每次只能交换一个字节,而int是4个字节(x86),所以就需要循环4次。

运行结果(通过名字首字母排序):

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值