Advanced Pointers - 3 (In-depth understanding of C language)

The source code of the article is here and you need to get it yourself: gitee

1. Function pointer

As mentioned in the previous article ( Advanced Pointers - 1 ):
Integer pointer - pointer to integer int*
character pointer - pointer to character char*
array pointer - pointer to array int arr[10]—>int (*parr)[]10 = &arr;


So:
a function pointer is a pointer variable pointing to a function. Function pointers can be used to call functions and pass parameters like normal functions. The function pointer is defined as:
function return value type (*pointer variable name) (function parameter list);


For example:

int (*p) (int, int);

This statement defines a pointer variable p, which can point to a function whose return value type is int and has two integer parameters.

The array pointer stores the address of the array, and
the function pointer should also store the address of the function
. So does the function have an address?
Let's take a look at the following piece of code:

#include <stdio.h>
void test()
{
    
    
 printf("hehe\n");
}
int main()
{
    
    
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

Output result:
Insert image description here
The output is two addresses, these two addresses are the addresses of the test function. So if we want to save the address of our function, how do we save it? Let's look at the code below:

void test()
{
    
    
 printf("hehe\n");
}
//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

First of all, to be able to give the storage address, it requires pfun1 or pfun2 to be a pointer. So which one is the pointer? the answer is:


pfun1 can be stored . pfun1 is first combined with *, indicating that pfun1 is a pointer. The pointer points to a function. The pointed function has no parameters and the return value type is void.


Additional attention is needed:

Array name: the address of the first element of the array
& (array name): the address of the entire array
(function) and & (function name) are both the address of the function, there is no difference

Let's read two interesting pieces of code below:

//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);

Code 1: (*(void (*)())0)();
This code is an expression called by a function pointer . First, (void (*)())0 casts the integer literal value 0 to the function pointer type void (*)(). Here void (*)() represents a pointer type pointing to a function with no return value and no parameters . The * operator is then used to dereference this function pointer, that is, to obtain the function pointed to by the pointer. Finally, the () operator calls the function.
The meaning of this code is to call the function with address 0.

代码2:void (*signal(int, void (*)(int)))(int);

This code is the function declaration statement . It declares a function named signal that accepts two parameters: an integer parameter and a pointer to a function that takes an integer parameter . The return type of the function signal is a pointer pointing to a function with integer parameters, and the return type is void.
Code 2 is too complex, how to simplify it:

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

2. Array of function pointers


An array of function pointers is an array whose elements are of function pointer type. A function pointer is a pointer variable pointing to a function, which can be used to call the corresponding function.


Here's a simple example of an array of function pointers (calculator):

2.1 Use switch statement

#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 x, y;
 int input = 1;
 int ret = 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:
	 	printf( "输入操作数:" );
	 	scanf( "%d %d", &x, &y);
	 	ret = add(x, y);
	 	printf( "ret = %d\n", ret);
	 	break;
	 case 2:
		 printf( "输入操作数:" );
		 scanf( "%d %d", &x, &y);
		 ret = sub(x, y);
		 printf( "ret = %d\n", ret);
		 break;
	 case 3:
		 printf( "输入操作数:" );
		 scanf( "%d %d", &x, &y);
		 ret = mul(x, y);
		 printf( "ret = %d\n", ret);
		 break;
	 case 4:
		 printf( "输入操作数:" );
		 scanf( "%d %d", &x, &y);
		 ret = div(x, y);
		 printf( "ret = %d\n", ret);
		 break;
	 case 0:
	 	printf("退出程序\n");
	 	breark;
	 default:
	 	printf( "选择错误\n" );
	 	break;
	 }
 } while (input);
 
	 return 0;
}

2.2 Using function pointer array

#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 x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = {
    
     0, add, sub, mul, div }; //转移表
	while (input)
	{
    
    
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("请选择:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
    
    
			printf("输入操作数:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else if (input == 0)
		{
    
    
			printf("退出\n");
			break;
		}
		{
    
    
			printf("输入有误\n");
			continue;
		}

		printf("ret = %d\n", ret);
	}
	return 0;
}

This makes the function more convenient when expanding

3. Pointer to array of function pointers


A pointer to an array of function pointers is a pointer that points to the first address of an array of function pointers. This pointer can be used to access elements in the function pointer array and call the corresponding function.


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;
}

In summary:
1. Pointers to arrays of function pointers can help simplify code that accesses such arrays, and can keep a reference to the array so that it can be modified when needed.

2. Pointers to function pointer arrays can help us better manage and operate function pointer arrays, thereby achieving more advanced and scalable programming.

4. Callback function


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.


4.1 To explain the callback function, we need to use the qsort function:

What is the qsort function? (The internal core algorithm of the qsort function is quick sort, and it is suitable for sorting any type of data)
Let’s first take a look at what the Cplusplus official website says:
The link is here: The qsort function
Insert image description here
Insert image description here
Insert image description here
can be seen from the introduction of the qsort function, the fourth parameter of qsort It's about the callback function.
After becoming familiar with the qsort function, let's first demonstrate the use of the qsort function:

#include <stdio.h>
//qosrt函数的使用者得实现一个比较函数
int int_cmp(const void * p1, const void * p2)
{
    
    
 return (*( int *)p1 - *(int *) p2);
}
int main()
{
    
    
	 int arr[] = {
    
     1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	 int i = 0;
	 
	 qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
	 for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
	 {
    
    
	 	printf( "%d ", arr[i]);
	 }
	 printf("\n");
	 return 0;
}

Let me explain this code:

int int_cmp(const void * p1, const void * p2)
{
    
    
    return (*(int *)p1 - *(int *)p2);
}

This is a comparison function int_cmp, its function is to provide a method for comparing two elements for the qsort function. The prototype of the comparison function must be consistent with the requirements of qsort. In this example, the parameter of the comparison function is a pointer of type const void *, which represents the address of the element to be compared. Inside the function, we convert these pointers to int * type and compare the values ​​pointed to by the pointers. The function returns an integer indicating the relative order between two elements.

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

In the main function, we define an integer array arr, which contains some unsorted integers.
Next, we call the qsort function to sort the array. The qsort function accepts four parameters: the starting address of the array to be sorted, the number of elements in the array, the size of each element, and a pointer to the comparison function. In this example, we passed the starting address of the array arr to qsort and calculated the number of elements in the array and the size of each element. The last parameter is a pointer to the comparison function int_cmp.

After sorting, we use a loop to traverse the array and print the sorted results using the printf function.

Finally, we print a newline character and return 0, indicating that program execution ended successfully.

4.2 Use the callback function to simulate the implementation of qsort (using bubbling method):

#include <stdio.h>
int int_cmp(const void * p1, const void * p2)
{
    
    
	 return (*( int *)p1 - *(int *) p2);
}
void _swap(void *p1, void * p2, int size)
{
    
    
	 int i = 0;
	 for (i = 0; i< size; i++)
	 {
    
    
	 char tmp = *((char *)p1 + i);
	 *(( char *)p1 + i) = *((char *) p2 + i);
	 *(( char *)p2 + i) = tmp;
	 }
}
void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
    
    
	 int i = 0;
	 int j = 0;
	 for (i = 0; i< count - 1; i++)
	 {
    
    
		 for (j = 0; j<count-i-1; j++)
		 {
    
    
			 if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
			 {
    
    
			 	_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
			 }
		 }
	 }
}
int main()
{
    
    
 int arr[] = {
    
     1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
 //char *arr[] = {"aaaa","dddd","cccc","bbbb"};
 int i = 0;
 bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), int_cmp);
 for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++)
 {
    
    
	 printf( "%d ", arr[i]);
 }
 	printf("\n");
 	return 0;
}

Let’s explain this code below:
I won’t talk about the int_cmp function. I just explained it above. Let’s talk about the _swap function and bubble function :

4.2.1 _swap

void _swap(void *p1, void * p2, int size)
{
    
    
	 int i = 0;
	 for (i = 0; i< size; i++)
	 {
    
    
	 char tmp = *((char *)p1 + i);
	 *(( char *)p1 + i) = *((char *) p2 + i);
	 *(( char *)p2 + i) = tmp;
	 }
}

Parameter analysis:
p1: pointer to the first element.
p2: Pointer to the second element.
size: The size of the element in bytes.

Internal implementation of the function:
1. First, the _swap function uses an integer variable i to iterate through each byte of the element.

2. In each iteration, it offsets the p1 and p2 pointers by i bytes respectively through pointer arithmetic to access the corresponding bytes of the two elements.

3. Using casts of character pointers, we convert these pointers to char * type for byte-wise access and exchange.

4. Create a temporary variable tmp to store the value of the byte pointed to by the p1 pointer.

5. Update the byte value pointed by the p1 pointer to the byte value pointed by the p2 pointer.

6. Update the byte value pointed to by the p2 pointer to the value of the temporary variable tmp.

7. In this way, the element values ​​pointed to by the p1 and p2 pointers are exchanged.

4.2.2 bubble

void bubble(void *base, int count , int size, int(*cmp )(void *, void *))
{
    
    
	 int i = 0;
	 int j = 0;
	 for (i = 0; i< count - 1; i++)
	 {
    
    
		 for (j = 0; j<count-i-1; j++)
		 {
    
    
			 if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)
			 {
    
    
			 	_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size);
			 }
		 }
	 }
}

Parameter analysis:
base: pointer to the array to be sorted. The void * type is used here to represent a universal pointer, which can point to any type of array.
count: The number of elements in the array to be sorted.
size: The size of each element in bytes.
cmp: Function pointer used to compare two elements.

Internal implementation of the function:
1. First, the bubble function uses two loops to traverse the array. The outer loop controls the number of rounds that need to be compared, and the inner loop is used to perform comparison and exchange operations of adjacent elements.

2. In each round of the inner loop, the value range of j is from 0 to count - i - 1, where i represents the current round number.

3. Use the comparison function cmp to compare two adjacent elements. The return value of the comparison function is greater than 0, which means that the previous element is greater than the next element and needs to be exchanged.

4. If exchange is required, call the auxiliary function _swap to exchange the values ​​of the two elements. Note that what is passed here is the address pointing to the element (implemented through pointer arithmetic and type conversion) and the size of the element.

5. After the inner loop ends, the comparison and exchange operations of the current round are completed, and the largest element is swapped to the end of the current round.

6. The outer loop is executed repeatedly, and each iteration will exchange the largest element in the current round to the corresponding position until all rounds of comparison and exchange operations are completed.

7. Finally, the elements in the array are sorted according to the specified comparison rules.

5. sizeof operator and strlen function


sizeof

1. sizeof is an operator in C language, not a function.

2. It is used to get the memory size (in bytes) occupied by a data type or variable.

3. The form of sizeof is sizeof(expression), where expression can be a data type, variable, or expression.

4. sizeof returns a size_t type value, indicating the memory size of the given expression.

5. sizeof is calculated at compile time, it does not perform actual calculation or access on the expression.


strlen

1. strlen is a library function in C language, which is located in the header file <string.h>.

2. It is used to obtain the length of a string, that is, the number of characters in the string (excluding the null character \0).

3. The form of strlen is strlen(str), where str is a null-terminated character array (string).

4. strlen returns a size_t type value, indicating the length of the given string.

5. strlen traverses the characters in the string at runtime until it encounters a null character ('\0'), and counts the number of characters.


sizeof operator and strlen function and differences

1. sizeof is an operator that can be used to get the size of any data type or variable, including basic types, custom types, and arrays. It is calculated at compile time and does not require access to the actual data.

2. strlen is a library function specially designed to handle character arrays (strings) terminated by null characters. It traverses the character array at runtime until it encounters a null character, counting the number of characters.

3. sizeof returns the number of bytes occupied by the data type or variable, while strlen returns the length of the string, that is, the number of characters.

4. sizeof can be used for statically allocated and dynamically allocated data, while strlen can only be used for character arrays terminated by null characters.

5. The result of sizeof is determined at compile time, while the result of strlen is determined at runtime.


Summary: sizeof and strlen both have important uses in C language. sizeof is used to get the size of a data type or variable, while strlen is used to get the length of a string. The main differences between them are calculation timing, applicable scope and return value type.

Guess you like

Origin blog.csdn.net/originalHSL/article/details/131606552