[C Language] Advanced Pointers (2) - Explanation of callback functions and how to use the qsort function

Table of contents

1. Array of function pointers

1.1. What is a function pointer array?

 1.2. Purpose of function pointer array: transfer table

2. Extension: pointer to an array of function pointers

3. Callback function

3.1. Introduction to callback functions

 3.2. Case of callback function: qsort function

3.2.1. Review of bubble sort

 3.2.1. What is the qsort function?


1. Array of function pointers

1.1. What is a function pointer array?

What is an array of function pointers ? First of all, the subject is an array , and an array is a storage space that stores data of the same type. Then we have already learned about pointer arrays, such as:

char* arr[5] ————Character pointer array, it is an array that stores character pointers.

int* arr[5] ———— Integer pointer array, it is an array that stores integer pointers.

Suppose there is a usage scenario where I need to store the addresses of several functions in an array. How should I store them? Let me introduce to you: function pointer array

int Add(int x, int y)
{
	return x + y;
}

int Sub(int x, int y)
{
	return x - y;
}

int main()
{
	int (*pf1)(int, int) = &Add;  //pf1和pf2是函数指针
	int (*pf2)(int, int) = ⋐
	//数组中存放类型相同的多个数组
	int (*pfArr[4])(int, int) = { &Add,&Sub };  //pfArr就是函数指针数组

	return 0;
}

The writing method of function pointer array is very similar to that of function pointer. You only need to add a square bracket [ ] after the name.

Note: Because an array is a storage space that stores data of the same type , a function pointer array can only store the function address of a function whose return type and parameter type are consistent.

 1.2. Purpose of function pointer array: transfer table

Use C language to implement a calculator function (addition, subtraction, multiplication and division):

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 menu()
{
	printf("*******************************\n");
	printf("******   1.add   2.sub    *****\n");
	printf("******   3.mul   4.div    *****\n");
	printf("******   0.exit           *****\n");
	printf("*******************************\n");
}

int main()
{
	int input = 0;
	int a = 0;
	int b = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入2个操作数:");
			scanf("%d %d", &a, &b);
			ret = add(a, b);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入2个操作数:");
			scanf("%d %d", &a, &b);
			ret = sub(a, b);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入2个操作数:");
			scanf("%d %d", &a, &b);
			ret = mul(a, b);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入2个操作数:");
			scanf("%d %d", &a, &b);
			ret = div(a, b);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default:
			printf("选择错误,重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

    Although the above code can implement a calculator function, it can be found that this code is particularly redundant , with many repeated parts. If you need to add one more function, you need to add one more case, causing the code to become more and more complex. It is long and has more and more repeated parts. This is a very bad coding habit. So is there any way to solve it?

    In fact, we can find through observation that these functions have some characteristics, that is, except for the different function names, the return type and parameter type are consistent.

    Since everything else is the same except for the function name, can we use an array of function pointers to transform the code?

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 menu()
{
	printf("*******************************\n");
	printf("******   1.add   2.sub    *****\n");
	printf("******   3.mul   4.div    *****\n");
	printf("******   0.exit           *****\n");
	printf("*******************************\n");
}

int main()
{
	int input = 0;
	int a = 0;
	int b = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		//创建一个函数指针数组
		int (*pfArr[])(int, int) = { NULL,add,sub,mul,div };
						    //为了使数组下标与菜单序号对应起来,在0下标处放置一个NULL
		if (input == 0)
		{
			printf("退出计算器\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入2个操作数:");
			scanf("%d %d", &a, &b);
			ret = pfArr[input](a, b); //下标访问数组中的函数并调用
			printf("ret = %d\n", ret);
		}
		else
		{
			printf("选择错误,重新选择\n");
		}
	} while (input);
	return 0;
}

As you can see, it can also be accomplished using an array of function pointers. If you need to add other functions in the future, you only need to change the menu, then write the function to implement the function, and then put the function into the function pointer array. And we call the function pointer array used in this scenario a transfer table .

This means that using an array of function pointers not only greatly improves the quality of the code, but also greatly reduces maintenance costs.

2. Extension: pointer to an array of function pointers

A pointer to an array of function pointers is a pointer that points to an array whose elements are all function pointers  .

How to define?

void test(const char* str)
{
	printf("%s\n", str);
}
int main()
{
	//函数指针pfun
	void (*pfun)(const char*) = test;
	//函数指针的数组pfunArr
	void (*pfunArr[5])(const char* str);
	pfunArr[0] = test;
	//指向函数指针数组pfunArr的指针ppfunArr
	void (*(*ppfunArr)[5])(const char*) = &pfunArr;
	return 0;
}

void (*(*ppfunArr)[5])(const char*)

void (*( *ppfunArr )[5])(const char*), ppfunArr is first combined with *, so it is a pointer.

void (* (*ppfunArr)[5] )(const char*), combined with [5], indicates that the pointer points to an array of size 5, and the type stored in each array is the function pointer void (*)(const char *).

Of course, the function pointer and array pointer mentioned here is already very in-depth content. There are very few usage scenarios. It is only used to expand your understanding. If you don’t understand it, don’t worry too much.

 

3. Callback function

3.1. Introduction to callback functions

The callback function has a very high status in C language and is very important. The callback function relies on the function pointer . Only with the function pointer can the callback function be implemented. When the calculator function used the function pointer array to call the addition, subtraction, multiplication and division functions, the addition, subtraction, multiplication and division functions were called callback functions.

A callback function is a function called through a function pointer. If you pass a function pointer (address) as a parameter to another function, and when this pointer is used to call the function it points to, we say it is a callback function. The callback function is not called directly by the implementer of the function, but is called by another party when a specific event or condition occurs to respond to the event or condition.

 Let’s use the calculator function as an example again:

Observing the code, we can find that only the function calling part is different. So can we define a cacl() function and pass the addition, subtraction, multiplication and division functions as parameters to the calc function, so that the addition, subtraction, multiplication and division functions can be called in the calc function?

Modified code based on this idea:

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 menu()
{
	printf("*******************************\n");
	printf("******   1.add   2.sub    *****\n");
	printf("******   3.mul   4.div    *****\n");
	printf("******   0.exit           *****\n");
	printf("*******************************\n");
}

void calc(int(*pf)(int, int))
{
	int a = 0;
	int b = 0;
	int ret = 0;
	printf("请输入2个操作数:");
	scanf("%d %d", &a, &b);
	ret = pf(a, b);
	printf("%d\n", ret);
}

int main()
{
	int input = 0;
	do
	{
		menu();
		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;
}

[Illustration]

The calc function can be understood as a transfer station . Whatever function address is passed to me as a parameter, I will call the function.

Tip: In previous blogs , I have come to a conclusion : when a function pointer calls the function pointed to, it can call the function directly as the function name without writing *, and the * sign here is actually just a decoration, also for the purpose of Take care of the usage habits of beginners , so the result obtained when adding a lot of * signs to dereference is still the correct result.

That is, (*pf)(a,b) is equivalent to pf(a,b).

If you want to know more thoroughly, you can go to my previous blog to read the function pointer part. (Link: click to go )

 3.2. Case of callback function: qsort function

3.2.1. Review of bubble sort

 For convenience of comparison, let’s review bubble sort first:

//冒泡排序算法
//给一组整型数据,然后使用冒泡排序对数据进行升序排序。

void bubble_sort(int arr[], int sz)
{
	//趟数
	int i = 0;
	for ( i = 0; i < sz - 1; i++)
	{
		//每一趟冒泡排序的过程
		int j = 0;
		for ( j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}

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

The above is the implementation of bubble sorting, but you can see that this bubble sorting is actually flawed. Its parameters are of type int, which limits it to only sorting integer data! The qsort function that will be discussed here is a function that can be used to sort any type of data.

 3.2.1. What is the qsort function?

qsort is a library function that uses quick sort at the bottom to sort data. Header file: <stdlib.h>

This function can be used directly to sort any type of data.

Of course, in addition to quick sort, there are many sortings, such as: bubble sort, selection sort, Hill sort, merge sort, etc.

qsort function definition prototype:

void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

  • void* base: the address of the first element of the array to be sorted
  • size_t num: the number of elements of the array to be sorted
  • size_t size: In bytes, the size of an element in the array to be sorted.
  • int (*compar)(const void*, const void*): function pointer, pointing to a function, used to compare two elements, created and encapsulated by the user.

 Why is void* used in the formal parameters of the comparison function:

void* is a pointer without a specific type. It cannot perform dereference operators or +- integer operations. It is used to store the address of any type of data (it can be understood as a trash can. It can hold anything. It can be used when needed. Then cast to the required type) . Only void* is allowed to store the address of any type of data. If it is a pointer of other types, the compiler will report an error. It is precisely because void* is used when defining the qsort function that the qsort function can sort any type of data.

The most important thing when using the qsort function is the last parameter . This parameter determines the rules for the qsort function to compare two elements. Here first write a comparison function cmp_int for sorting integer data :

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

 Requirements for comparison functions:

  • When the element pointed to by p1 is greater than the element pointed by p2, a number greater than 0 is returned.
  • When the element pointed to by p1 is equal to the element pointed by p2, return 0
  • When the element pointed to by p1 is smaller than the element pointed by p2, a number less than 0 is returned.

 【Complete code】

 Use the qsort function to sort integer data.

int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}

int main()
{
	int arr[] = { 10,9,8,7,6,5,4,3,2,1 };
	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]);
	}
	return 0;
}

In the same way, the qsort function sorts structure type data (the following example sorts by age in the structure) :

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

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

int main()
{
	struct Stu arr[] = { {"zhangsan",20},{"lisi",21},{"wangwu",22} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_struct);
	return 0;
}

 【operation result】

It can be found that sorting by age is indeed completed.


If you think the author's writing is good, please give the blogger a big like and support. Your support is my biggest motivation for updating!

If you think the author's writing is good, please give the blogger a big like and support. Your support is my biggest motivation for updating!

If you think the author's writing is good, please give the blogger a big like and support. Your support is my biggest motivation for updating!

Guess you like

Origin blog.csdn.net/zzzzzhxxx/article/details/132941465