今日收获(C语言)

一.存放函数指针的数组

在上一篇博客中,我简单介绍了什么是函数指针以及有什么用。

如果我想要把几个同一类型的函数地址存到一个数组里,就要用到存放函数指针的数组了:

int(*arr[4])(int,int)={Add,Sub,Mul,Div};

arr是数组名,右边的[4]代表数组里有四个元素,其余的就是它的类型(int(*)(int,int)),也是数组里每个元素的类型。

下面以简单的加法器来应用存放函数指针的数组:

#include<stdio.h>
void menu()
{
	printf("************************************\n");
	printf("******    1.加法    2.减法    ******\n");
	printf("******    3.乘法    4.除法    ******\n");
	printf("******    0.退出              ******\n");
	printf("************************************\n");
}
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;
}
int main()
{
	int input = 0;
	int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };
	while (1)
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if (input == 0) return 0;
		else if (input >= 1 && input <= 4)
		{
			int a = 0, b = 0;
			printf("请输入两个数字:");
			scanf("%d %d", &a, &b);
			int ret = arr[input](a, b);
			printf("%d\n", ret);
		}
		else printf("请重新选择!\n");
	}
	return 0;
}

有几点值得注意:

int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };为什么第1个元素是0?

这要结合下面的代码:

int ret = arr[input](a, b);

我希望当我输入1的时候执行加法,输入2的时候执行减法.....而数组下标是从0开始的,所以干脆就把第一个元素赋为0 。

arr[input]会找到第input个元素,而这个元素本身就是函数的地址,所以可以直接arr[input](a, b)执行不同函数的功能。

二.qsort函数以及冒泡排序

qsort是一个专门用来对任意类型数据排序的函数,使用的时候只需要规定比大小的规则就好了(之所以需要规定比大小的规则,是因为不同类型数据比较方法是不一样的),其余的排序qsort函数会自动完成。

qsort有4个参数,分别是:起始地址,元素个数,每个数据的大小(以字节为单位),自定义比大小函数的地址。

下面用qsort函数写一些代码:

#include<stdio.h>
#include<stdlib.h>  //qsort函数的头文件
int my_cmp(const void* e1, const void* e2)   //加const是为了保证只“用”数据而不允许更改
{                                            
	return *(int*)e1 - *(int*)e2;                  
}
int main()
{
	int arr[10] = { 3,8,4,6,2,9,0,5,1,7 };
	qsort(arr, 10, sizeof(arr[0]), my_cmp);
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

对于第四个参数:自定义比大小函数,它的返回值被规定为int类型,只有当返回值大于0时,qsort才会交换两个数据的顺序。在上面的代码中,如果把e1和e2交换顺序,就是从大到小排序 。

对于int my_cmp(const void* e1, const void* e2),void类型保证了它能接收任意类型的数据地址,而void类型的指针是无法被解引用的,要先进行强制类型转换,转换成要比较的数据的类型,然后再解引用找到对应的值。

刚才说了,qsort可以对任意类型的数据进行排序,那对结构体成员排序也很容易:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[20];
	int age;
};
int my_cmp(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int main()
{
	struct stu s[3] = { {"zhangsan",24},{"lisi",56},{"wangwu",45} };//初始化结构体数组
	qsort(s, 3, sizeof(s[0]), my_cmp);
	int i = 0;
	for (i = 0;i < 3;i++)
	{
		printf("%s\n", s[i].name);
	}
	return 0;
}

对于return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);

先把e1和e2强制类型转换成结构体类型的指针,然后用“->”操作符找到姓名,用strcmp函数比较字符串大小。

类比qsort函数里的参数,我们能不能用冒泡排序的思路写出一个能对任意类型数据排序的函数?

代码如下:

#include<stdio.h>
#include<stdlib.h>
int my_cmp(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0;i < width;i++)
	{
		char temp = *buf1;
		*buf1 = *buf2;
		*buf2 = temp;
		buf1++;
		buf2++;
	}
}
void bubble_sort(void* base, int n, int width, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for (i = 0;i < n - 1;i++)
	{
		int flag = 1;
		int j = 0;
		for (j = 0;j + 1 < n - i;j++)
		{
			if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0)
			{
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
				flag = 0;
			}
		}
		if (flag == 1) return;
	}
}
int main()
{
	int arr[10] = { 3,8,4,6,2,9,0,5,1,7 };
	bubble_sort(arr, 10, sizeof(arr[0]), my_cmp);
	int i = 0;
	for (i = 0;i < 10;i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

1.void bubble_sort(void* base, int n, int width, int (*cmp)(const void* e1, const void* e2)),这与qsort函数里的参数类型是一样的,其中int (*cmp)(const void* e1, const void* e2)就是之前提到过的函数指针,用来接收一个函数的地址。

2.我在冒泡排序的内部定义了一个flag=1,其实这是为了提高效率,如果第一次遍历一遍元素后没有任何元素被交换(flag无法被赋成0),此时直接结束这个函数,因为这代表元素是有序的,不需要排序。

3.if (cmp((char*)base + j * width, (char*)base + (j + 1) * width) > 0),cmp是上面定义的函数指针,使用这个函数的时候需要传入两个地址,但是为什么地址写成这样?

(char*)base + j * width,base是起始地址,先把它强制类型转换成char类型指针,这可以保证在对它加减整数的时候是以1个字节为单位移动,j*width则代表指针移动的长度,j 是下标,width是每个元素的字节数,相乘就是指针移动的长度,此时指向的就是待排序的元素。把它作为地址传到函数里。

4.my_cmp函数怎么写要看想要排序的数据的类型。

5.Swap函数:Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);我在调用Swap的时候把width(每个元素的字节数)也传进去了,在Swap函数中,用for循环以字节数为单位进行交换,每个元素的字节数决定了for循环的次数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值