[Advanced C] Callback function (advanced pointer 2, detailed explanation, must-see for beginners)

Table of contents

6. Array of function pointers

6.1 Simple Calculator

6.2 Function pointer array to realize calculator

7. A pointer to an array of function pointers (just for understanding)

8. Callback function

8.1 Understanding of callback functionsEdit

8.1.1 Improving a Simple Calculator with Callbacks

8.2 Use of qsort library function

8.2.1 Bubble sort

8.2.2 The concept of qsort

8.3 Bubble sorting idea to realize qsort


         This article includes but is not limited to a summary of knowledge points such as arrays of function pointers , pointers to arrays of function pointers, and callback functions. Inheriting the above pointer advanced (1) summary of knowledge points, portal -- >  http://t.csdn.cn/mgVGJ

Pointer Advanced (3): Analysis and summary of pointer and array written test questions, Portal -->  http://t.csdn.cn/aKVsj 

        If there are any mistakes, please point and correct, thank you for your visit!

6. Array of function pointers

An array is a storage space that stores the same type of data, so we have already learned about pointer arrays,
for example:
int * arr [ 10 ];
//Each element of the array is an int*

Then store the address of the function in an array, then this array is called an array of function pointers, how to define the array of function pointers ?

int ( * parr1 [ 10 ])();//1
int * parr2 [ 10 ]();//2
int ( * )() parr3 [ 10 ];//3

 The answer is: parr1 //1

Parr1 is first combined with [] , indicating that parr1 is an array, what is the content of the array?
Is a function pointer of type int (*)() . --> For a variable to remove its name, it is its type.
int my_strlen(const char* str)
{
	return 0;
}
int main()
{
  //指针数组
	char* arr[10];
  //数组指针
	int arr2[5] = { 0 };
	int(*p)[5] = &arr2;//p是一个指向数组的指针变量

	//函数指针
    int (*pf)(const char*) = my_strlen;//pf是一个指向函数的函数指针变量
	
	//函数指针数组-存放函数指针的数组
	int (*pfArr[10])(const char*);
    //1.pf先与[10]结合,代表这是一个数组,数组有10个元素;
    //2.去掉pf[10],剩余int (*)(int,int)为数组每个元素的类型--函数指针类型

	return 0;
}

The following three writing methods are all equivalent
1. *(*pf)("abcdef");
2. pf ("abcdef");
3. my_strlen("abcdef");

Let's illustrate by implementing a calculator function, where the function pointer array is used:

6.1 Simple Calculator

According to our normal thinking, writing a simple calculator to realize the function of addition, subtraction, multiplication and division of integers should be roughly written as this code:

Then execute it and see how it goes:

The above problem is that after inputting 0, it does not immediately print "Exit Calculator" and then end the program, but continues to input two operands before printing the information, and also prints a 6 by the way, there must be a problem with this code. Let's improve it a little bit.

//写一个计算器能完成整数的+ - * /
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;
}
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 x = 0;
	int y = 0;
	int ret = 0;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Add(x, y);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Sub(x, y);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数:>");
			scanf("%d %d",&x,&y);
			ret = Mul(x, y);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			ret = Div(x, y);
			printf("%d\n", ret);
			break;
		case 0:
			printf("退出计算器\n");
			break;
		default :
			printf("选择错误\n");
			break;
		}
	}while(input);
	return 0;
}

Execute the code and continue testing:

 The problem is solved, but a new problem is found:

 There are too many switch case statements in the above code. When I want to add functions such as: << >> & | ^ && || in this counter, there will be more and more case statements. How should I correct it? ?

At this time, an array of function pointers will be used .

example:

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 (*pf[4])(int, int) = {Add,Sub,Mul,Div };
	                        // 0    1   2   3
	int i = 0;
	for (i = 0; i < 4; i++)
	{
		int ret = pf[i](8, 4);
		printf("%d\n", ret);
	}

	return 0;
}

This code uses an array of function pointers, stores the addresses of the four operation functions in the array, and then traverses the array through a loop, calls the four operation functions in turn to perform calculations and output the results. This approach can reduce code duplication and improve code maintainability.

 6.2 Function pointer array to realize calculator

//函数指针数组实现计算器
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;
}
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 x = 0;
	int y = 0;
	int ret = 0;//用于接收值
   
    //转移表 - 函数指针的数组
	int (*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };
							 //   0    1   2   3    4
	//NULL的作用是为了占用0下标,然后让函数指针数组的元素下标对应上菜单的功能
	do {
		//菜单
		menu();
		//进行选择
		printf("请选择:>");
		scanf("%d", &input);
		//用函数指针数组代替switch
		if (input == 0)
		{
			printf("退出计算器\n");
			return 0;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d",&x,&y);
			ret = (***pfArr[input])(x, y);//其实比函数指针的用法就多了一个[input]
			 //跟函数指针一样,调用函数的时候 * 是没有任何意义的
			printf("%d\n",ret);
		}
		else
		{
			printf("选择错误\n");
		}
	} while (input);
	return 0;
}

An array of function pointers is an array whose elements are function pointers. Each function pointer points to a specific function, and the corresponding function can be called through the function pointer. The function pointer array can be used to solve the problem of code redundancy, and can easily traverse and call different functions, thereby simplifying the structure and logic of the code, and improving the maintainability and scalability of the code.

Purpose of array of function pointers: transfer table

7. A pointer to an array of function pointers (just for understanding)

//指向函数指针数组的指针
int Add(int a, int b)
{
	return a + b;
}
int Sub(int a, int b)
{
	return a - b;
}
int main()
{   //函数指针
	int(*pf)(int, int) = Add;
	//函数指针数组
	int (*pfArr[4])(int, int) = { Add,Sub };
	//指向函数指针数组的指针
	 int (*(*ppfArr)[4])(int,int) = &pfArr;

	 return 0;
}

How to understand a pointer to an array of function pointers:

8. Callback function

concept

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

There are many parts with the same logic and redundant code.

 At this time, you need to use the callback function to solve the problem:

8.1 Understanding of callback functions

8.1.1 Improving a Simple Calculator with Callbacks

#include<stdio.h>
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;
}
//将冗余重复部分用Calc函数封装(只用了一个函数),通过这个函数里面传入的函数指针调用计算器函数
void Calc(int(*pf)(int, int))//pf函数指针,传过来哪个函数地址就用哪种类型计算
{
	int x = 0;
	int y = 0;
	int ret = 0;
	printf("请输入两个操作数:>");
	scanf("%d %d", &x, &y);
    ret = pf(x, y);
	printf("%d\n", ret);
}
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 ret = 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;
}

Summarize:

Callback function: the function passed as a parameter, the four functions of Add, Sub, Mul, and Div are callback functions,

The Calc function is a tool

8.2 Use of qsort library function

8.2.1 Bubble sort

Review the process of bubble sorting:

The idea of ​​​​bubble sorting: compare two adjacent elements, assuming that they are to be arranged in ascending order

How to write the code for bubble sorting:
①The number of times is determined by the number of elements

②Comparative logarithm determined by the number of trips         

③Two elements are exchanged in pairs and arranged in ascending order

//冒泡排序 
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;
			}
		}
	}
}
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1,0};
	//排序
	//使用
	int sz = sizeof(arr) / sizeof(arr[0]);

	bubble_sort(arr, sz);

	print_arr(arr, sz);
	return 0;
}

insufficient:

But the disadvantage of this bubble sort is also obvious, that is, only integer int can be sorted

If you encounter floating-point, structure and other types of data, you can't arrange them, so how can you solve them?

At this time, qsort will be used.

8.2.2 The concept of qsort

qsort-- quicksort

It is a library function, which is used to sort the library function using the quick sort method

The advantage of qsort is

1. Ready-made

2. Can sort any type of data

 

qsort can sort any type of data

1. Compare the size of 2 integers, > < ==

//qsort函数的使用者提供这个函数
//qsort 默认是升序
int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
    //排成倒序的话,什么都不用动,只需要return *(int*)p2 - *(int*)p1;//调换顺序即可
}
void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
test1()
{
	int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用qsort来排序整型数组,这里就要提供一个比较函数,这个比较函数能够比较2个整数的大小
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print_arr(arr, sz);
}
int main()
{
   test1();
}

 

2. Compare 2 strings, strcmp --> review string knowledge: http://t.csdn.cn/V7E9a

3. Compare 2 structure data (students: Zhang San, Li Si) specify the standard of comparison

Review the knowledge of structure object access members: http://t.csdn.cn/DVEVj

//测试qsort 排序结构体数据
struct Stu {
	char name[20];
	int age;
};
void print_arr1(struct Stu* arr, int sz)//打印年龄
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		//printf("%d ", (*(arr + i)).age);
		//printf("%d ", (arr+i)->age);
		printf("%d ", arr[i].age);

	}
	printf("\n");
}
void print_arr2(struct Stu*arr, int sz)//打印姓名
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%s ", (arr+i)->name);
		//printf("%s ", (*(arr+i)).name);
		//printf("%s ", arr[i].name);

	}
	printf("\n");
}
//按照年龄来比较
int cmp_stu_by_age(const void*p1,const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;

}
int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void test2()
{
	struct Stu s[] = { {"zhangsan",30},{"lisi",25},{"wangwu",50} };
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按照年龄来排序
	print_arr1(s, sz);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr1(s, sz);
	//测试按照名字来排序
	/*print_arr2(s,  sz);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr2(s, sz);*/
}

print age:

print gender:

8.3 Bubble sorting idea to realize qsort

Use the idea of ​​bubble sorting to implement a bubble sorting function bubble_sort() similar to the function of qsort

Each parameter is composed of:
base, num, width, cmp function, elem1 and elem2

Next, explain the design ideas of each parameter of qsort:

①Why base is defined as void* type?

void* is a general pointer type in C language , which can point to any type of data . In the qsort function, since different types of data need to be sorted, pointers of void* type need to be used to accept various types of data. The base parameter is a pointer to the first element of the array to be sorted. Since it points to data of any type, using void* is the most general.

Example:

int a = 10;

void* p = &a;

advantage:

There is no specific type of pointer, so it can receive any type of address

shortcoming:

*p  //-->  void*的指针不能解引用操作符

p++  //-->  因为不知道是什么数据类型,不能直接++

Correct usage:

*(int*)p;//也可以转化成其它类型

②Why should num be designed as size_t?

  • size_t is an unsigned integer type in C language, which is used to represent size, length and index value, and it is suitable to use it to represent the number of array elements.
  • The number of array elements is a non-negative value, and it is more reasonable to use the unsigned type size_t to avoid negative numbers.

  • Using the specialized size_t type is more indicative of the semantics of this parameter than a simple unsigned int - it represents a size or length, not an integer.

③Why width should be designed as size_t

In the qsort function, the width parameter indicates the size of each array element (in bytes)

  1. Width represents a concept of "size", using the size_t type can express the semantics of this parameter more clearly.

  2. The unit of width is byte, and size_t is usually an unsigned integer type, and there will be no negative numbers, so it is more suitable for representing positive values.

  3. width requires a range large enough to represent the size of any data type. The size_t type is platform-dependent, but is usually machine-word-sized, which satisfies the size requirements of most data elements.

  4. When accessing array elements, the index position needs to be multiplied by the element size to obtain the address offset. Since the index is of type size_t, the result of multiplying the two should also be of type size_t to avoid overflow problems.

④ Why design a function pointer cmp?

  1. qsort itself is a general sorting function, which cannot know the type of elements to be sorted, so it cannot directly compare two elements. Using function pointers can leave the implementation of comparison operations to the user.

  2. Through function pointers , users can implement comparison functions for their own data types and encapsulate specific comparison logic. This increases the versatility and flexibility of qsort.

  3. For different data types, the logic of the comparison operation may be different. Using function pointers can avoid writing a lot of conditional branch logic in qsort.

  4. Function pointers provide an extension mechanism. If the user needs to change the logic of the comparison operation, he only needs to pass in a new function pointer, without changing the qsort function itself.

⑤Why are elem1 and elem2 types const void*?

  1. qsort sorts data of any type, so the comparison function needs to be able to handle elements of any type. Using void* can point to any type of data, and const is used to indicate that the content pointed to by the pointer should not be modified.

  2. qsort sorts data of any type, so the comparison function needs to be able to handle elements of any type. Using void* can point to any type of data, and const is used to indicate that the content pointed to by the pointer should not be modified.

  3. When calling qsort, you can directly convert the address of the array element into a const void* type and pass it to the comparison function to simplify the call.

  4. The qsort function itself does not need to understand the specific type of the element, just pass the void* pointer to the comparison function, and the comparison function converts and interprets the pointer content.

Start the simulation implementation: the idea of ​​​​bubble sorting big questions does not need to be changed, only the sorting method needs to be adjusted

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
}
//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width,
	int (*cmp)(const void* p1, const void* p2))
{
    //冒泡排序大题思路不需要改变,只需要对排序方式进行即可
	//要确定趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;//假设已经有序了
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//两个相邻的元素比较
			//arr[j] arr[j+1]
			if (cmp((char*)base + j * width, (char*)base+(j+1)*width)>0)
			{
				//交换
				flag = 0;
				Swap((char*)base+j*width,(char*)base+(j+1)*width,width);
			}
		}
	}


}

void print_arr(int arr[], int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test3()
{
	int arr[] = { 3,1,5,2,4,9,8,6,5,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	printf("排序前:");
	print_arr(arr, sz);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	printf("排序后:");
	print_arr(arr, sz);
}

qsort's analysis of the code:

How to exchange data:

Why is this place written as char*?

In the bubble_sort function, since the data type pointed to by base is not known, it needs to be cast to char* type, so that the step size of the pointer is 1. In this way, when adding and subtracting pointers, the step size of the pointers can be controlled according to the width parameter, so as to realize the sorting of any data type.

Note: width means 1 byte for char, but not for other types. Width sets different data types according to different parameters and controls the pointer step size.

Exchange process: 1, 2, 3, 4 represent the order 

 The overall flow of the code:

Sort by:

Sort age and name by newly defined bubble sort

struct Stu {
	char name[20];
	int age;
};
int cmp_stu_by_age(const void*p1,const void* p2)
{
	return ((struct Stu*)p1)->age - ((struct Stu*)p2)->age;

}
int cmp_stu_by_name(const void* p1, const void* p2)
{
	return strcmp(((struct Stu*)p1)->name, ((struct Stu*)p2)->name);
}
void Swap(char* buf1, char* buf2, int width)
{
	int i = 0;
	for (i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
//假设排序为升序
//希望这个bubble_sort函数可以排序任意类型的数据
void bubble_sort(void* base, size_t num, size_t width,
	int (*cmp)(const void* p1, const void* p2))
{
	//要确定趟数
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		int flag = 0;//假设已经有序了
		//一趟冒泡排序的过程
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			//两个相邻的元素比较
			//arr[j] arr[j+1]
			if (cmp((char*)base + j * width, (char*)base+(j+1)*width)>0)
			{
				//交换
				flag = 0;
				Swap((char*)base+j*width,(char*)base+(j+1)*width,width);
			}
		}
	}


}
void print_arr3(struct Stu* s, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", (s + i)->age);
		//printf("%s ", (*(arr+i)).name);
		//printf("%s ", arr[i].name);

	}
	printf("\n");

}
void print_arr4(struct Stu* s, int sz)
{
	int i = 0;
		for (i = 0; i < sz; i++)
		{
			printf("%s ", (s+i)->name);
			//printf("%s ", (*(arr+i)).name);
			//printf("%s ", arr[i].name);
	
		}
		printf("\n");

}
void test4()
{
	struct Stu s[] = { {"zhangsan",30} ,{"lisi",25},{"wangwu",50}};
	int sz = sizeof(s) / sizeof(s[0]);
	//测试按年龄来排序
	/*print_arr3(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	print_arr3(s, sz);*/
	//测试按照名字来排序
	print_arr4(s, sz);
	bubble_sort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	print_arr4(s, sz);

}

Sort by name:

 sort by age

This article is over here, please correct me if there are any mistakes, and then come down to this detailed summary about sizeof and strlen:, pointer and array pen test question analysis summary, portal -->  http://t.csdn .cn/aKVsj

Welcome everyone to visit.

Guess you like

Origin blog.csdn.net/weixin_65186652/article/details/131976796