[C Language] Advanced Pointers (3) - Simulating the implementation of the qsort function and analysis of written test questions on pointers and arrays

Table of contents

1. Simulate the implementation of qsort function

1.1. Review of qsort function

1.2. Simulate the implementation of qsort function 

2. Analysis of written test questions on pointers and arrays

2.1. One-dimensional array

2.2. Character array


1. Simulate the implementation of qsort function

1.1. Review of qsort function

To simulate and implement the qsort function, you must understand the parameters and usage of the qsort function.

Let’s first review 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.

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 comparison function, used to compare two elements, created and encapsulated by the user.

Why is void* used in formal parameters:

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.

1.2. Simulate the implementation of qsort function 

Use the [bubble sort] algorithm to simulate and implement a sorting function  bsort  , which can be used to sort any type of data.

First, use bubble sort to sort integer data:

void bsort(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]);
	bsort(arr, sz);

	int i = 0;
	for ( i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);

	}
	printf("\n");
	return 0;
}

        Transform this code based on bubble sorting of integer data.

        The first part of the transformation is the function parameters. The function parameters here are hard-coded and can only be sorted by int type . Therefore, in order to allow other types of data to be passed into the bsort function for sorting, we need to use pointers to receive them. Pass in parameters.

        Parameter 1: As for the selection of pointers, only  void*  is the most suitable, because it is used to store the address of any type of data (it can be understood as a trash can, it can hold anything, and it is forced to be converted to the required type when needed. ).

       Parameter two: According to the definition of the library function qsort, the second parameter is the number of elements of the array to be sorted, so we also use size_t num  to store the number of elements of the array to be sorted.

Currently, we can know the starting address (void* base) and the number of elements (num) through parameter one and parameter two, but just knowing the starting address and the number of elements is not enough, because we don’t know what an element has. How big, how many bytes need to be skipped at one time, 5? 10?

        Parameter three: Therefore, a parameter is needed to record the size of an element size_t size.

At this point, we first focus on the inside of the function. The number of loops and the number of times inside the function does not need to be modified. Only the exchange area in the red box needs to be modified, because the size of the integer can be used with ><= symbols. Comparison, but structure data cannot be compared directly using ><= signs. Sorting an integer requires an integer method, and sorting a structure requires a structure method. Therefore, if you need to sort various types, the comparison method of the swap area cannot be fixed , which requires the user to create a comparison function by himself. to fulfill.

        Parameter four : Here we only need to use the function pointer int (*cmp) (const void* e1, const void* e2)   to receive the comparison function passed by the user. e1 and e2 are both pointers, each storing the address of an element to be compared.

 Modify the red box area:

Note that when passing in the base parameter in the cmp function, base needs to be cast to char*, because only char has the shortest step size and can satisfy all types of exchanges. Assuming it is an int type, +1 skips 4 bytes directly. If you want to exchange a char type or short type data, the exchange cannot be done.

 [Complete code for exchanging int type data]

swap(char* buf1, char* buf2, size_t size)
{
    一个字节一个字节地交换
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}


//模拟库函数qsort
void bsort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for ( i = 0; i < num - 1; i++)
	{
		int j = 0;
		for ( j = 0; j < num - 1 - i; j++)
		{
			//if(arr[j]>arr[j+1])
			if (cmp((char*)base + size * j, (char*)base + size * (j + 1)) > 0) //大于0时,证明j的元素大于j+1的元素,所以要交换位置
			{
				//交换
				swap((char*)base + size * j, (char*)base + size * (j + 1), size);
			}
		}
	}
}


//用户自行创建
int cmp_in(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]);
	bsort(arr, sz,sizeof(arr[0]),cmp_in);

	int i = 0;
	for ( i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);

	}
	printf("\n");
	return 0;
}

[Illustration]

In the same way, structure type data can also be exchanged. 

 [Complete code for exchanging structure type data]

swap(char* buf1, char* buf2, size_t size)
{
	//一个字节一个字节地交换
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bsort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for ( i = 0; i < num - 1; i++)
	{
		int j = 0;
		for ( j = 0; j < num - 1 - i; j++)
		{
			//if(arr[j]>arr[j+1])
			if (cmp((char*)base + size * j, (char*)base + size * (j + 1)) > 0) //大于0时,证明j的元素大于j+1的元素,所以要交换位置
			{
				//交换
				swap((char*)base + size * j, (char*)base + size * (j + 1), size);
			}
		}
	}
}


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

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

int main()
{
	struct Stu arr[] = { {"zhangsan",22},{"lisi",26},{"wangwu",20} };

	int sz = sizeof(arr) / sizeof(arr[0]);
	bsort(arr, sz,sizeof(arr[0]),cmp_stu_age);


	printf("\n");
	return 0;
}

In the example, they are sorted by age. Use debug to check that the swap is indeed done:

If you want to sort in descending order, you only need to  exchange the return e1 and e2 in the cmp function  created and encapsulated by the user. The following takes cmp_int as an example:

swap(char* buf1, char* buf2, size_t size)
{
	//一个字节一个字节地交换
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}

void bsort(void* base, size_t num, size_t size, int (*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	for ( i = 0; i < num - 1; i++)
	{
		int j = 0;
		for ( j = 0; j < num - 1 - i; j++)
		{
			//if(arr[j]>arr[j+1])
			if (cmp((char*)base + size * j, (char*)base + size * (j + 1)) > 0) //大于0时,证明j的元素大于j+1的元素,所以要交换位置
			{
				//交换
				swap((char*)base + size * j, (char*)base + size * (j + 1), size);
			}
		}
	}
}

int cmp_in(const void* e1, const void* e2)
{
	return *(int*)e2 - *(int*)e1;  //进行【降序】排序
}

int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	int sz = sizeof(arr) / sizeof(arr[0]);
	bsort(arr, sz,sizeof(arr[0]),cmp_in);


	return 0;
}

2. Analysis of written test questions on pointers and arrays

Calculate the value of the output of the following operation

2.1. One-dimensional array

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a + 0));
printf("%d\n", sizeof(*a));
printf("%d\n", sizeof(a + 1));
printf("%d\n", sizeof(a[1]));
printf("%d\n", sizeof(&a));
printf("%d\n", sizeof(*&a));
printf("%d\n", sizeof(&a + 1));
printf("%d\n", sizeof(&a[0]));
printf("%d\n", sizeof(&a[0] + 1));

[Answer] The result of running in 32-bit environment

【Analysis】

printf("%d\n", sizeof(a));

sizeof (array name), the array name here represents the entire array, so the size of the entire array is calculated, 4 (number of int bytes) * 4 = 16.

printf("%d\n", sizeof(a + 0));

a is not placed alone inside sizeof, and there is no &, so the array name a is the address of the first element of the array, and a+0 is still the address of the first element. In a 32-bit environment, the address size is 4.

printf("%d\n", sizeof(*a));

 a is not placed alone inside sizeof, and there is no &, so the array name a is the address of the first element of the array. *a is the first element, the size is 4, special: *a == *(a+0) == a[0]

printf("%d\n", sizeof(a + 1));

 a is not placed alone inside sizeof, and there is no &, so the array name a is the address of the first element of the array, a+1 is the address of the second element, and in a 32-bit environment, the address size is 4.

printf("%d\n", sizeof(a[1]));

 a[1] is the second element of the array. What is calculated here is the size of the second element. Int is 4.

printf("%d\n", sizeof(&a));

 &a - is to take out the address of the array, but the address of the array is also an address. In a 32-bit environment, the address is 4. The essential difference between the address of the array and the address of the first element of the array is the difference in type, not the size.

printf("%d\n", sizeof(*&a));

 Dereference the array pointer to access the size of an array. The unit is bytes. It can also be understood as  *& canceling each other out , that is, sizeof(*&a) = sizeof(a) , sizeof(array name). The array name here represents the entire array. , so the size of the entire array is calculated.

printf("%d\n", sizeof(&a + 1));

 The address of &a array, &a+1 is still an address, which is 4.

printf("%d\n", sizeof(&a[0]));

 &a[0] is the address of the first element, and the size of the address is 4.

printf("%d\n", sizeof(&a[0] + 1));

 &a[0] is the address of the first element, &a[0]+1 is the address of the second element, with size 4.

2.2. Character array

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

[Answer] The result of running in 32-bit environment

【Analysis】

printf("%d\n", sizeof(arr));

 The array name arr is placed alone inside sizeof, and the size of the entire array is calculated. 6.

printf("%d\n", sizeof(arr + 0));

arr is the address of the first element ==&arr[0], which is 4.

  • The size of a pointer variable has nothing to do with its type. Regardless of the type of pointer variable, the size is always 4/8 bytes.
  • Pointer variables are used to store addresses. How much space is needed to store addresses? The size of a pointer variable is just a few bytes.
  • In a 32-bit environment, the address is 32 binary bits and requires 4 bytes, so the size of the pointer variable is 4 bytes.
  • In a 64-bit environment, the address is 64 binary bits and requires 8 bytes, so the size of the pointer variable is 8 bytes.
printf("%d\n", sizeof(*arr));

 arr is the address of the first element, *arr is the first element, and the size is 1.

printf("%d\n", sizeof(arr[1]));

 arr[1] is the second element, and its size is 1.

printf("%d\n", sizeof(&arr));

 &arr is the address of the array, sizeof(&arr) is 4

printf("%d\n", sizeof(&arr + 1));

&arr+1 is the address after skipping the array, and is the address, which is 4.

printf("%d\n", sizeof(&arr[0] + 1));

 The address of the second element is 4.

After reading sizeof, let’s look at a set of strlen

printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

 [Answer] The result of running in 32-bit environment

1. Random value

2. Random value

3、err

4、err

5. Random value

6. Random value -6

7. Random value -1

【Analysis】

Note: The question uses the creation method of char arr[] = {'a','b','c','d','e','f'};, which will not automatically add it at the end. \0, and strlen will only stop when it encounters \0

printf("%d\n", strlen(arr));

arr is the address of the first element, that is, starting from the first element of arr, look for \0. Since we don’t know where the subsequent \0 is, it is a random value.

printf("%d\n", strlen(arr + 0));

 arr is the address of the first element, arr+0 is still the address of the first element, the same as the previous question, it is a random value.

printf("%d\n", strlen(*arr));

 arr is the address of the first element, *arr is the first element, srtlen needs to receive an address, and what is passed here is a character. From the perspective of strlen, it is considered that the 'a'-97 passed in is the address, and 97 is used as the address. Direct access is illegal access, so the program will report an error.

printf("%d\n", strlen(arr[1]));

 The second element address of arr is 'b', which is the same as the previous question. The illegal access program reports an error.

printf("%d\n", strlen(&arr));

 &arr represents the address of the entire array, looking backwards for \0, so it is a random value.

printf("%d\n", strlen(&arr + 1));

  &arr represents the address of the entire array, &arr + 1 represents skipping the entire array (6 bytes) and looking backward for \0, so it is a random value -6.

printf("%d\n", strlen(&arr[0] + 1));

 &arr[0] represents the address of the first element, &arr[0] + 1 skips one element and searches backward for \0, so it is a random value -1.

Let's change it to char arr[] = "abcdef"; let's take a look again

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

[Answer] The result of running in 32-bit environment

 

【Analysis】

printf("%d\n", sizeof(arr));

 sizeof (array name) represents the entire array. sizeof will also include \0, so it is 7.

printf("%d\n", sizeof(arr + 0));

 arr+0 represents the address of the first element, which is 4.

printf("%d\n", sizeof(*arr));

 *arr represents the first element, which is of type char, so it is 1.

printf("%d\n", sizeof(arr[1]));

 The second element, so it's 1.

printf("%d\n", sizeof(&arr));

&arr represents the address of the entire array. The address is 4

printf("%d\n", sizeof(&arr + 1));

 &arr represents the address of the entire array, &arr + 1 represents skipping the entire array, and the address is 4.

printf("%d\n", sizeof(&arr[0] + 1));

 The address of the first element is 4.

Replace with srtlen

printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

【Answer】

1、6

2、6

3、err

4、err

5、6

6. Random value

7、5

【Analysis】

printf("%d\n", strlen(arr));

 Count how many elements there are before \0, which is 6.

printf("%d\n", strlen(arr + 0));

 arr+0 is equal to the address of the first element. Count how many elements there are before \0, which is 6.

printf("%d\n", strlen(*arr));

*arr represents the first element. Directly accessing  the element as an address is illegal access, so the program will report an error.

printf("%d\n", strlen(arr[1]));

 Similar to the previous question, illegal access to the program reports an error.

printf("%d\n", strlen(&arr));

 &arr represents the address of the entire array, looking backward to \0, so it is 6.

printf("%d\n", strlen(&arr + 1));

  &arr represents the address of the entire array, &arr + 1 represents skipping an entire array. After reaching \0, there is no way to know where the next \0 is, so it is a random value.

printf("%d\n", strlen(&arr[0] + 1));

 &arr[0] + 1 represents the address of the second element, looking backward to \0 equals 5.


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/133023732