第三十六天学习记录:C语言进阶:指针详解Ⅳ

文章讨论了C语言和C++中函数指针的概念,包括指向函数指针数组的指针以及回调函数的原理。在C语言中,回调函数通常通过函数指针作为参数传递,而在C++中,回调函数可以是函数对象,更加灵活。文中还给出了多个示例,如计算器程序、字符串比较和排序函数qsort的使用,展示函数指针的实际应用。
摘要由CSDN通过智能技术生成

指向函数指针数组的指针

指向函数指针数组的指针是一个指针,指针指向一个数组,数组的元素都是函数指针。

	int(*pfArr[4])(int, int);//pfArr是一个数组-函数指针的数组
	int(*(*ppfArr)[4])(int, int) = &pfArr;
	//ppfArr是一个数组指针,指针指向的数组有4个元素
	//指向的数组的每个元素的类型是一个函数指针int(*)(int,int)

回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行相应。

问:
C语言的回调函数和C++的回调函数本质是一样的吗?
ChatAI:
C语言和C++中的回调函数的本质是类似的,但在具体实现和使用时,两者还是略有不同之处的。

回调函数是在函数调用过程中传递一个函数指针(function pointer)给被调用的函数,并由被调用函数进行回调,即在内部执行函数指针所指向的函数。在C语言中,回调函数被广泛应用于事件处理、信号处理、多线程编程等方面。而在C++中,回调函数一般被称为回调(Callback)或函数对象(Functor),主要应用于STL库中的算法和容器等实现中。

在C语言中,回调函数通常作为一个函数指针参数传递给被调用函数,通过指针调用进行回调。函数指针可以指向普通的全局或局部函数,也可以指向结构体中的成员函数,这使得回调函数可以方便地扩展为面向对象的形式。在C语言中,回调函数的使用通常需要注意函数指针类型的匹配问题,以及函数指针的生命周期管理等问题。

在C++中,回调函数往往被实现为函数对象,即可被调用的运算符重载函数。使用回调函数对象时,可以把函数对象当作一个普通的参数传递给被调用函数,不需要考虑指针类型匹配和生命周期管理等问题。此外,C++的回调函数对象还可以方便地重载operator(),使得函数对象可以像函数一样调用,并支持函数对象的继承和多态等高级功能。

因此,虽然C语言和C++中的回调函数本质相似,但在具体实现和使用时,两者还是有区别的,而且C++的回调函数对象更为灵活和高级。

例题:
计算器变种版

#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;
}
void Calc(int(*pf)(int, int))
{
	int x = 0;
	int y = 0;
	printf("请输入两个操作数:");
	scanf("%d%d", &x, &y);
	printf("%d\n", pf(x, y));
}

int main()
{
	int input = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add             2:sub \n");
		printf(" 3:mul             4:div \n");
		printf("*************************\n");
		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;
}

例题2:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

void print(char* str)
{
	printf("Chung:%s", str);
}

void test(void(*p)(char*))
{
	printf("test\n");
	p("Perfect");
}

int main()
{
	test(print);
	return 0;
}

输出:
在这里插入图片描述
例题3:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>

void BubbleSort(int arr[], int sz)
{
	int flag = 0;
	int i = 0;
	int j = 0;
	int tmp = 0;
	for (i = 0; i < sz-1; i++)
	{
		j = 0; flag = 1;
		for (j = 0; j < sz - 1 - i;j++)
		if (arr[j] > arr[j + 1])
		{
			tmp = arr[j];
			arr[j] = arr[j + 1];
			arr[j + 1] = tmp;
			flag = 0;
		}
		if (flag == 1)
		{
			break;
		}
	}
}
//qsort-可以排序任意类型的数据
struct Stu
{
	char name[20];
	int age;
};

int main()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	struct  Stu s[3] = { {"Zhangsan",20},{"Lisi",30},{"Wangwu",10} };
	float f[] = { 9.0,8.0,7.0,6.0 ,5.0,4.0};
	BubbleSort(arr, sz);
	BubbleSort(f, sz);
	for (i = 0; i < sz; ++i)
	{
		printf("%d", arr[i]);
	}
	return 0;
}

void* 的步长是多少?
ChatAI答:
void*指针是一种通用类型的指针,可以指向任何类型的数据,但是由于void类型没有存储空间,因此对void*类型进行算术运算或者偏移指针需要使用合适的步长以确保指针指向正确的地址。

在C和C++中,void*指针的步长是由所指向的类型进行决定的。例如,如果指向的是int类型的数据,那么步长就是sizeof(int);如果指向的是double类型的数据,那么步长就是sizeof(double),以此类推。

需要注意的是,对于void*类型的指针,编译器不会进行任何类型检查或隐式转换。因此,使用void*指针时需要确保指针类型和实际类型匹配,否则可能会导致程序出现未定义的行为。建议在使用void*指针前,先将指针强制转换为合适的类型以进行正确的内存操作。

//void* 类型的指针 可以接收任意类型的地址
//void* 类型的指针 不能进行解引用操作
//void* 类型的指针 不能进行±整数的操作

问:

//为什么C语言的字符串无法用"=="比较,但Qt的QString可以直接用"=="比较?

ChatAI答:
在C语言中,使用"=="运算符对字符串进行比较时,比较的是两个字符串的地址是否相等,也就是比较它们是否指向同一个存储位置,这显然并不是我们想要的字符串比较方式。正确的比较方式是使用库函数strcmp,该函数会按照字典序逐个比较字符串中的字符,以确定它们的相对大小关系。

而在Qt框架中,QString类定义了"=="运算符的重载实现,允许直接对QString对象进行比较,比如:

QString str1 = "hello";
QString str2 = "hello";
if (str1 == str2) {
  // 两个字符串相等
}

这种比较方式会比较两个字符串的内容是否相等,而不是它们的地址。在实现中,QString类重载了"=="运算符来比较两个字符串,可以直接返回字符串内容的比较结果。不同于C语言,Qt框架有自己的库函数,QString类中也提供了compare()函数来进行字符串比较。

例题4:
qsort的使用:

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Stu
{
	char name[20];
	int age;
};

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

int cmp_int(const void* e1, const void*e2)
{
	//比较两个整型值的
	return *(int*)e1 - *(int*)e2;
}

void test1()
{
	int arr[] = { 1,3,5,7,9,2,4,6,8,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < sz; ++i)
	{
		printf("%d ", arr[i]);
	}
}

int cmp_float(const void* e1, const void*e2)
{
	if (*(float*)e1 == *(float*)e2)
	{
		return 0;
	}
	else if (*(float*)e1> *(float*)e2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}

void test2()
{
	float f[] = { 9.0,8.0,7.0,6.0 ,5.0,4.0};
	int sz = sizeof(f) / sizeof(f[0]);
	qsort(f, sz, sizeof(f[0]), cmp_float);
	int i = 0;
	for (i = 0; i < sz; ++i)
	{
		printf("%f ", f[i]);
	}
}

int cmp_stu_by_age(const void* e1, const void*e2)
{
	return ((struct  Stu*)e1)->age - ((struct  Stu*)e2)->age;
}

int cmp_stu_by_name(const void* e1, const void*e2)
{
	//比较名字就是比较字符串,字符串比较不能直接用><=来比较,应该用
	return strcmp(((struct  Stu*)e1)->name , ((struct  Stu*)e2)->name);
}

void test3()
{
	struct  Stu s[3] = { {"Zhangsan",20},{"Lisi",30},{"Wangwu",10} };
	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);
}

int main()
{
	test1();
	printf("\n");
	test2();
	printf("\n");
	test3();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值