17.0、C语言——指针详解(3)

17.0、C语言——指针详解(3)

来看一个C语句【该语句出自《C陷阱与缺陷》这本书】:

int main() {
    (*(void(*)())0)();
    return 0;
}	

        我们先把这段代码拆开来看 void(*)() 这其实是一个函数指针类型,该函数返回值为void,参数为无参,那么( void(*)() ) 0 表示把 0 强制类型转换为 void(*)() 类型,之后再解引用符 * ,表示获取该函数,最后一个括号表示调用该函数

        总结:把 0 强制类型转换成一个 函数指针类型,该函数指针指向的函数是一个无参无返回值类型的函数,然后当 0 变成一个指针类型之后对他进行解引用操作,去调用以 0 为地址处的该函数

再来看第二个C语句:

int main() {
    void(*signal(int, void(*)(int)))(int);
    return 0;
}

        还是一样把这个语句拆开来看,signal( int, void(*)(int) ) 是一个函数,该函数有两个参数,第一个参数类型为 int,第二个参数类型为函数指针,把函数名和参数去掉后剩下的void(*)(int)就是signal函数的返回类型~
        总结:signal是一个函数声明;signal函数的参数有2个,第一个是int。第二个是函数指针,该函数指针指向的函数的参数是int,返回类型是void;signal函数的返回类型也是一个函数指针:该函数指针指向的函数的参数是int,返回类型是void

函数指针 typedef 重命名;向上面那个代码很复杂我们来简化一下:

int main() {
	//简化 重命名为 pfun_t
	typedef void(*pfun_t)(int);
	pfun_t signal(int,pfun_t);
	return 0;
}


        当我们 解引用符函数指针去调用函数的时候可以发现,不管我们用不用解引用符 都可以正常的调用到函数,代码如下所示:

int add(int a,int b) {
	return a + b;
}
int main() {
	int (*padd)(int, int) = add;
	printf("%d",   padd(1, 2));
	printf("%d", (*padd)(1, 2));
	return 0;
}


函数指针数组:        

定义方式:int ( * parr1 [ 10 ] ) ();【这里 parr1 先与[ ] 结合成为数组,数组元素的类型是int(*)() 类型的函数指针】

定义的方式和指针差不多就不过多介绍了,直接上代码:

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(*parr[10])(int, int) = {add,sub,mul,div};
	//函数调用
	int i = 0;
	for (i = 0;i<4;i++) {
		printf("%d\n",parr[i](3,2));
	}
	return 0;
}

函数指针数组的用途:转移表
例子:(简易计算器)

#define _CRT_SECURE_NO_WARNINGS 1
#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 input = 0;
	int x = 0;
	int y = 0;
	int (*p[])(int, int) = {0,add,sub,mul,div};
	int arrLength = sizeof(p) / sizeof(p[1]);
	do {
		printf("请选择>\n");
		printf("***** 1.add  2.sub  3.mul  4.div *****\n");
		scanf("%d",&input);
		if (input >= 1 && input < arrLength) {
			printf("请输入两个操作数\n");
			scanf("%d%d", &x, &y);
			int ret = p[input](x, y);
			printf("result = %d\n", ret);
		}
		else if (input == 0) {
			printf("退出");
		}
		else {
			printf("输入的数字有误\n");
		}
	} while (input);
	return 0;
}

回调函数:

        回调函数就是一个通过函数指针调用的函数,如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

        回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用,用于对该事件或条件进行相应。

 还是上面的计算器的例子(这里用回调函数的方式实现):

#define _CRT_SECURE_NO_WARNINGS 1
#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 Calc(int (*p)(int,int)) {
	int x = 0;
	int y = 0;
	printf("请输入两个操作数\n");
	scanf("%d%d",&x,&y);
	return p(x, y);
}
int main() {
	int input = 0;
	int ret = 0;
	do {
		printf("请选择>\n");
		printf("***** 1.add  2.sub  3.mul  4.div *****\n");
		scanf("%d",&input);
		switch (input)
		{
		case 1:
			printf("result = %d\n", Calc(add));
			break;
		case 2:
			printf("result = %d\n", Calc(sub));
			break;
		case 3:
			printf("result = %d\n", Calc(mul));
			break;
		case 4:
			printf("result = %d\n", Calc(div));
			break;
		case 0:
			printf("退出");
			break;
		default:
			printf("输入有误\n");
			break;
		}
	} while (input);
	return 0;
}

回调函数可以很好的解决代码冗余的问题

指向函数指针数组的指针:

指向函数指针数组的指针是一个 指针 ,指针指向一个 数组,数组的元素都是 函数指针
代码如下:

int main() {
	int arr[10] = { 0 };
	int(*p)[10] = &arr;//取出数组的地址
	int(*p2)(int,int);//函数指针
	int(*parr[10])(int,int);//parr是一个数组-函数指针的数组
	int(*(*pparr)[10])(int,int) = &parr;//pparr是一个指向[函数指针数组]的指针
	//pparr是一个数组指针,指针指向的数组有10个元素
	//指向的数组的每个元素的类型是一个函数指针int(*)(int,int)
	return 0;
}

int ( * ( *pparr ) [10] )(int,int) = &parr;
        * 先与 pparr 结合说明这是一个指针,然后指向 [ ] 数组,把  ( *pparr ) [10] 去掉之后就是指向的数组的每个元素的类型 -> int ( * )(int,int)

qsort()库函数:

void qsort(void* base,
		   size_t num,
	       size_t width,
		   int(*cmp)(const void* e1,const void* e2));

第一个参数是:需要比较的数组 地址;
第二个参数是:数组中的元素个数;
第三个参数是:每个元素占用空间的大小;
第四个参数是:用来比较两个元素的函数地址【函数中的参数是传递两个比较元素的地址】;

【void :表示没有规定特定类型的参数,所以可以接收任意类型的参数】
        我们之前说到过,指针类型决定了 解引用符能够访问的内存空间,但是 void* 类型的指针由于没有指定指针类型所以不能够 进行 解引用符操作~【不然会报错 非法的间接寻址】
        当然 指针 也不能进行 + -  整数的操作,因为+1 , -1 也需要指定指针的类型,不然也不知道要跳过多少字节

在qsort()库函数中规定:

当第一个元素小于第二个元素的时候,让比较函数返回 负数
当第一个元素等于第二个元素的时候,让比较函数返回   0
当第一个元素大于第二个元素的时候,让比较函数返回 正数

实例代码如下所示【在调用qsort()库函数的时候记得引头文件:#include<stdlib.h>】:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int com_int(const void* e1, const void* e2) {
	//这里由于void类型的指针不能进行解引用符操作,
	//所以先将他们强制类型转换为int*类型再进行解引用符操作
	return *(int*)e1 - *(int*)e2;
}
int main() {
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), com_int);
	int i = 0;
	for (i = 0; i < sz; i++) {
		printf("%d\n", arr[i]);
	}
	return 0;
}

    输入结果为 0 1 2 3 4 5 6 7 8 9 ,排序成功~
【注意:元素比较的函数返回值类型规定必须是整数】

如果比较的是一个结构体的数组:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<String.h>
struct Stu
{
	char name;
	int age;
};
int cmp_stu_by_age(const void* e1,const void* e2) {
	return ((struct Stu*)e1)->age - ((struct Stu*)e1)->age;
}
int cmp_stu_by_name(const void* e1, const void* e2) {
	return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e1)->name);
}
int main() {
	struct Stu s[3] = { {"xiaoming",20},{"xiaolan",18}, {"xiaohong",26}};
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	return 0;
}

【这里注意如果比较的是字符串的大小要用 strcmp()库函数去比较】

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值