In depth C language 2 - Advanced pointer

Posted by timcclayton on Sun, 10 Oct 2021 07:19:04 +0200

Welcome back to continue learning C language. After the data storage section, we come to the most important content in C language, pointer. Pointer can be said that in the eyes of beginners of C language or C + +, we are old friends who can't see their heads up. The data structure pointer of C language version is also the protagonist, Therefore, after we talked about pointers before, we will open another section to specifically study pointers. Therefore, you can see that my previous blog may include several sections, but after entering into C language, the article will only focus on one theme, because we have reached the stage of really studying hard, and our ideas should only belong to the present, Let's start now.

1. Character pointer

We know that pointers, like variables, have many types. Different types of pointers are used to point to the space of different types of data, so it is obvious that the space that different types of pointers can point to is also limited. Let's first look at the character pointer. What will happen in memory if a string is pointed to by a pointer?

#cinlude <stdio.h>
int main()
{
	char ch = 'w';
	char* pc = &ch;//pc points to a constant character
	const char* p = "hello bit";//"hello bit" is a constant string, which is stored in the constant area of memory
	//The above expression is used to assign the address of the first character h of the constant string "hello bit" to p
	//*p = 'w';
	printf("%c", *p);
	printf("%s", p);
	return 0;
}

We now know that the original character pointer points to the first character stored in the string space when it is stored in the string. It does not mean that the string is placed in the pointer. When we dereference, we actually get the first character of the string. When we print it with% c, it is h, and when we print it with% s, it is printed from the effective position of the address to the invalid position of the address.

Then there is such an interview question:

#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    char *str3 = "hello bit.";
    char *str4 = "hello bit.";
    if(str1 ==str2)
 printf("str1 and str2 are same\n");
    else
 printf("str1 and str2 are not same\n");
       
    if(str3 ==str4)
 printf("str3 and str4 are same\n");
    else
 printf("str3 and str4 are not same\n");
       
    return 0;
}

  Why? Why don't two arrays with the same content want to wait? Why are two pointers like waiting? In fact, we know that except for those two special cases, the array name represents the first element address, so if str1 and str2 are directly compared, the first element addresses of the two arrays are actually compared. Of course, they are not equal, because their two roots have two different spaces. Although str3 and str4 are two pointers, they both point to each other The string "hello world." therefore, in memory, they point to the same memory space. If str3 and str4 are dereferenced at the same time, they will get the initial 'h', so str3 and str4 are equal, and they are maintaining a space together.

2. Array pointer

This concept must be the first time we have heard of it. We've heard of array pointers. What the hell are array pointers? I'm afraid you haven't read it all together. In C language, we do have the concept of array pointers. We read only the suffix. What the suffix is, it's what it is. So array pointers are pointers. They are pointers used to store arrays. Maybe we're still confused. To put it bluntly, it's before Array pointers are used to maintain a piece of space. Array pointers are used to maintain a series of continuous spaces, which is array pointers.

So how to define an array pointer?

int *p1[10];
int (*p2)[10];
//What are P1 and P2?

  What do you think is right? When we look at these two codes, we need to consider the priority of operators. We know that the brackets are highly prioritized. When parentheses are used, the expressions in parentheses are first calculated, which is already known in mathematics. Therefore, *p should be combined with advanced technology, so this is a pointer, and then int [10] Combination, so the second formula is the correct way to write array pointers. (note here that the priority of [] is higher than that of * sign, so () must be added to ensure that P is combined with * first)

&Array name vs array name

Your old friends appeared. Why do you want to talk about the array name repeatedly mentioned before? It's because we have a lot of content related to the array, so I think it's better to review this knowledge as a whole.

Then we know that & the array name is the address of the whole array, and writing only the array name only represents the address of the first element. When we define an array and print it in the form of% p respectively & the array name and the array name, we will find that their results are the same, because they all represent the same space, but when we both perform the + 1 operation, we will find that the results are different , suppose that the size of our array is 10, then & array name + 1 skips the whole array. At the end of the array, if only array name + 1, only one element of array type size is skipped.  

#include <stdio.h>
int main()
{
 int arr[10] = { 0 };
 printf("arr = %p\n", arr);
 printf("&arr= %p\n", &arr);
 printf("arr+1 = %p\n", arr+1);
 printf("&arr+1= %p\n", &arr+1);
 return 0;
}

OK, with these preparations, just, how to use the array pointer? Since the array pointer points to the array, the address stored in the array pointer should be the address of the array. Look at the code:

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//Assign the address of array arr to array pointer variable p
    //Just for demonstration, but we rarely write code like this
    return 0;
}

&Put arr into * p, which represents the p pointer. Now we need to maintain the whole array of arr, which is the way to use array pointers.

Use of an array pointer:

#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
void print_arr2(int (*arr)[5], int row, int col)
{
    int i = 0;
    for(i=0; i<row; i++)
   {
        for(j=0; j<col; j++)
       {
            printf("%d ", arr[i][j]);
       }
        printf("\n");
   }
}
int main()
{
    int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
    print_arr1(arr, 3, 5);
    //The array name arr represents the address of the first element
    //But the first element of the two-dimensional array is the first row of the two-dimensional array
    //So the arr passed here is actually equivalent to the address of the first row, which is the address of a one-dimensional array
    //You can use array pointers to receive
    print_arr2(arr, 3, 5);
    return 0;
}

When we pass array parameters, we can directly write type + array [] at the position of formal parameters , parameters can also be passed through pointers. If it is an array of integer type, we use an integer pointer to point to its first element address, and then enter the pointer into the function as a parameter. During calculation, we also use the forward and backward movement of the pointer to get the elements to be operated in the array. Then a two-dimensional array is defined in this code. We We know that a two-dimensional array is a pile of numbers, which has rows and columns, so we can also treat a two-dimensional array as a one-dimensional array, so each element of it is stored in a one-dimensional array, * arr represents three rows of elements, so we can transfer parameters by array pointer, and we can also achieve the effect of successfully accessing each element of the array.

3. Pointer array

We all know that when these two concepts overlap, we look at the suffix. This suffix is an array, so we know that the pointer array is an array used to store pointers. It can also be understood as storing a pile of arrays. Different from the general idea of an array, we just replace the data elements in it with pointers that can point to space.

So how is the pointer array defined? According to the way we analyzed array pointers just now, if the array pointer needs to be combined with the pointer first, I'll let it be combined with the array first. How? If you remove the brackets, will the priority be occupied by [] so that arr will be combined with [] first, which will become an array, and then combined with the previous * so that our definition of pointer array is realized.

char* arr1[5];//arr is an array of character pointers
int* arr2[5];//arr is an array of integer pointers

So how to use pointer array?

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int* arr3[4] = { &a,&b,&c,&d };
	for (int i = 0; i < 4; i++)
	{
		printf("%d ", *(arr3[i]));
	}
	return 0;
} 

  If the pointer array is used to store pointers, the four space pointer arrays we defined should be used to store the addresses of some variables. Because there are pointers in arr3, when we dereference the elements in arr3, we get the address of the element. The element is shaped, so we can print successfully in the form of% d.

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };

	int* parr[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", parr[i][j]);//p[i]==*(p+i)
			//parr[i][j]==*(parr[i]+j)
		}
		printf("\n");
	}
	return 0;;
}

The above code represents the definition method of two-dimensional pointer array. If the code just mentioned is the address of the stored variable, the parr in this code is the address of the array, which realizes that the two-dimensional array has rows and columns. After understanding these, I need to emphasize that we know that arr represents the address of the first element, Since it is an address, we can find the elements through the address. Therefore, when we print the elements in the array, we can write arr[i], or we can access the space we want to access through the address dereference * (arr+i). Therefore, can we think that arr[i]==*(arr+i)? Yes, it does comply with the syntax. Now that we return to the above code, can we print the elements in parr without writing parr[i][j]? It's too low. I've learned pointer. OK, let's convert parr[i][j] according to the analysis just now. We know that parr stores the address of the array. For example, we need the third element of the second array. We have to find the first array first. The form of pointer number group is int * parr []. Yes, when we * parr get the first element, That is, the address of the first array. When we add + j, it means that I want to access the first few elements in the second array, so it can be written as * (parr[i]+j). Of course, each printing method has no distinction between high and low status. It is OK to complete our image efficiently and correctly.

int main()
{
	const char* arr[5] = {"abcdef", "bcdef", "hehe", "haha", "zhangsan"};
	for (int i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

This code is very simple for us. Every time we print the elements in arr [], they are a string of characters, and each string of characters can be expressed as an array. This is the definition method and use method of pointer array.

After understanding this, let's take a look at these codes:

int arr[5];//One dimensional array
int *parr1[10];//Parr1 is combined with [] first, so parr1 is a pointer array
int (*parr2)[10];//Parr2 is combined with * first, so parr2 is an array pointer
int (*parr3[10])[5];//Parr3 combines with [] first, then * and then [] outside, so parr3 is an array that can hold five array pointers

4. Array parameter passing and pointer parameter passing

We talked about array pointer and pointer array, and we know that they can be combined. Let's go back and take a look at the array parameter transfer we talked about at the beginning. How many forms are there in the array transfer function?

//One dimensional array parameter transfer
void test1(int arr[])
{}
void test2(int arr[10])
{}
void test3(int* arr)
{}
void test4(int* arr[20])
{}
void test5(int** arr)
{}
int main()
{
	int arr[10] = { 0 };
	int* arr[20] = { 0 };
	return 0;
}

We can see that there are so many forms of array parameter transmission, which can be directly transmitted to the arr array, through the pointer (as mentioned earlier), through the array pointer, or through the secondary pointer. These can all realize the transmission of array parameters. Of course, it is recommended to choose a simple method. After all, although too complex methods can be realized, But it's still hard to understand.

//Two dimensional array
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
//Summary: for the design of two-dimensional array parameters and function parameters, only the first [] number can be omitted.
//Because for a two-dimensional array, you can't know how many rows there are, but you must know how many elements there are in a row.
//This is convenient for calculation.
void test(int *arr)//ok?
{}
void test(int* arr[5])//ok?
{}
void test(int (*arr)[5])//ok?
{}
void test(int **arr)//ok?
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
 return 0;
}

Note that the two-dimensional array can pass parameters except the secondary pointer, because the secondary pointer is used to store the pointer address, and the two-dimensional array has no direct relationship with the secondary pointer.

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

Parameter passing by the first level pointer can be accepted by the first level pointer. The reason is the same as that of parameter passing by the array name. The array name represents the address of the first element. When receiving, you can take the first level pointer to receive.

void test(int *ptr)
{}
int main()
{
	int a = 10;
	int arr[] = { 0 };
	int* p = &a;

	test(&a);
	test(arr);
	test(p);
	return 0;
}
//Secondary pointer transfer parameter
void test(int** ppa)
{}
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	int* arr[10];

	test(ppa);
	test(&pa);
	test(arr);
	return 0;
}
//This is how the secondary pointer passes parameters
void test(char **p)
{}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);
 test(ppc);
 test(arr);//Is this OK? Why?
 return 0;
}

  Please note that arr is an array of pointers, and all stored in it are pointers. Therefore, when the array name is used to directly pass parameters, the first element address is passed in, so a pointer needs to be taken to receive, so it is correct to pass arr.

5. Function pointer

We talked about array pointers and pointer arrays, but there are also function pointers in C language, so we found that pointers can be combined with anyone. We also found whether they are functions and arrays, which can be stored in memory blocks, have addresses, so they can be combined with pointers. What is a function pointer? How? What's the use?

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

  We found that when we print the address of a function in the form of% p, we can also get the same effect as printing arrays and variables. The hexadecimal address is printed on the screen. Then the function does have an address in memory. Can we take a pointer to it? The answer is yes. Let's take a look at how function pointers are defined.

void test()
{
 printf("hehe\n");
}
//Which of the following pfun1 and pfun2 has the ability to store the address of the test function?
void (*pfun1)();
void *pfun2();

We talked about the priority combination of operators earlier, so we know that if a pointer wants to point to a function, it must first be a pointer. Pfun1 has parentheses and is combined with * first, so it is a pointer and then combined with the outer void (), so pfun1 represents a function pointer, and pfun2 will be combined with () first, So it is a function, but the return value is void *, that is, pfun2 is a function that can return a pointer.

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//printf("%p", &Add);
	//printf("%p", Add);
	int(*pf)(int, int) = &Add;//pf is the address used to store the function. pf is the function pointer variable
	int ret = Add(4, 5);
	printf("%d\n", ret);
	ret = (*pf)(4, 5);
	printf("%d\n", ret);

	return 0;
}

Are the answers output by (* pf)(4,5) and Add(4,5) the same? We know that Add(4,5) is to calculate 4 + 5, and pf is a pointer to Add. Then we know that the space can also be accessed through the pointer, so the Add function can also be found by pf. Therefore, the answer to the calculation of the two expressions is the same, and the second representation method is the calling function realized by using the function pointer.

Now, I'm sure you'll know how to observe the superposition of many operators. Let's look at two interesting Codes:

//Code 1
(*(void (*)())0)();
//Code 2
void (*signal(int , void(*)(int)))(int);
typedef void(*pfun)(int);
int main()
{
	//This code is a function declaration
	//signal has two parameters, the first is int and the second is the function pointer type of void(*) ()
	//The return type of siganl is still the function pointer type of void(*) ()
	
	//void(*siganl(int, void(*)(int)))(int);
	pfun(*siganl(int, pfun))(int);

	return 0;
}

int main()
{
	//The code is a function call in which the 0 cast type is converted to a function address of void(*) ()
	//*(void (*) () 0 dereference to call the function at address 0. The called function has no parameters and the return type is void
	(*(void(*)())0)();
	return 0;
}

6. Function pointer array

We have entered a new concept, function pointer array. We still understand this concept according to the suffix. First, it must be an array, and then an array used to store function pointers. Therefore, each element in this array points to a function.

So how should it be defined?

int (*parr1[10]])();
int *parr2[10]();
int (*)() parr3[10];

We know that the type of integer array is int   [], the type of array pointer is int(*) [], the type of pointer array is int * [], and the type of function pointer is void(*) (). How to write the type of function pointer array? Is it OK to add an array of Peugeot [] after the function pointer, so only parr1 is correct in these three formulas. Firstly, the priority of [] is higher than *, so parr1 is first combined with [], which is called array, and then combined with *, which means that it stores pointers, and the outermost is the return type of the function and the parentheses used to pass parameters, This is the definition method of function pointer array, so what is its use?

When we wrote the project earlier, a main function called many functions. When we need the functions of some functions, we need to call it. After learning the function pointer array, can we directly use it to store these functions as the elements of the array? The answer is yes, then we will implement it with the simplest computer

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 meun()
{
	printf("***********************\n");
	printf("****1.Add*****2.Sub****\n");
	printf("****3.Mul*****4.Div****\n");
	printf("****0.exit*************\n");

}
int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		meun();
		printf("Please select:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
            printf("Enter the two numbers you want to calculate:");
            scanf("%d %d",&x,&y);
			Add();
			break;
		case 2:
            printf("Enter the two numbers you want to calculate:");
            scanf("%d %d",&x,&y);
			Sub();
			break;
		case 3:
            printf("Enter the two numbers you want to calculate:");
            scanf("%d %d",&x,&y);
			Mul();
			break;
		case 4:
            printf("Enter the two numbers you want to calculate:");
            scanf("%d %d",&x,&y);
			Div();
			break;
		case 0:
			printf("sign out");
			break;
		default:
			printf("error");
			break;
		}
	} while (input);
	return 0;
}

This is the implementation of a simple computer. Have we found that addition, subtraction, multiplication and division are encapsulated into a function and called with a switch statement, but can we optimize it after learning the function pointer array? Since the function pointer array is used to store function pointers, can you point to these four functions with pointers and then save them with the function pointer array? If so, how?

int main()
{
	int* arr[10];//Integer pointer array
	//Array of function pointers
    //1.
	int (*pf1)(int, int) = &Add;
	int (*pf2)(int, int) = &Sub;
	int (*pf3)(int, int) = &Mul;
	int (*pf4)(int, int) = &Div;
    //2.
	int (*pfArr[4])(int, int) = { Add,Sub,Mul,Div };//pfArr - array of function pointers
	return 0;
}

In this way, we can use the function pointer array to store the function pointer. Note that 12 methods are OK, but method 2 is more like an array, so we can put them into the computer just now to see what changes will be made.

int main()
{
	int input = 0;
	do
	{
		int x = 0;
		int y = 0;
		int ret = 0;
		meun();
		printf("Please select:");
		scanf("%d", &input);
		int (*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
		if (input == 0)
		{
			printf("sign out\n");
		}
		else if (input >= 1 && input <= 4)
		{
			printf("Enter two numbers:");
			scanf("%d %d", &x, &y);
			ret = pfArr[input](x, y);
			printf("%d\n", ret);
		}
		else
		{
			printf("Input error\n");
		}
	} while (input);
	return 0;
}

We only need to modify the main function and divide the case s in switch into elements of the function pointer array, so that 0 represents element 0, that is, exit, Add represents element 1, Sub represents element 2, Mul represents element 3 and Div represents element 4. When input is input, we can access the elements in pfArr with the value for input and pass in x and y.

7. Pointer to function pointer array

Well, here comes the dolly again. After learning the function pointer array, we found that the original function pointers and functions can be nested with each other. This time, we discuss another pointer outside the function pointer array, which means to create a pointer to the function pointer array, that is, we can access the elements in the function pointer array through this pointer, Of course, you can also modify, delete and other operations.

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int arr[10];
	int* arr[10];//Integer pointer array
	int* (*pa)[10] = &arr;//Pointer to integer pointer array
	int (*pfArr[5])(int,int) = &Add;//Array of pointers to functions
	int (*(*ppfArr)[5])(int, int) = &pfArr;//Pointer to array of function pointers
	return 0;
}

By defining the method, we can find that the pointer to the function pointer array is based on the function pointer array, and a layer of pointer is set outside, that is, ppArr is combined with [5] first to represent an array, then combined with * to represent a pointer array, and then combined with * outside to represent that it can point to a function pointer, which can be stored in the address of pfArr.

void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //Function pointer pfun
 void (*pfun)(const char*) = test;
 //Array of function pointers pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //Pointer to function pointer array pfunArr ppfunArr
 void (*(*ppfunArr)[10])(const char*) = &pfunArr;
 return 0;
}

These are the nested use of functions, arrays and pointers. You need to taste them carefully and understand the priority relationship between these operators.

8. Callback function

This will be another new concept, callback function. Most of you may not have heard of this concept. Let's take a look at the concept in the book: callback function is a function called through function pointer. If you pass the pointer (address) of a function as a parameter to another function, when the 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 by another party when a specific event or condition occurs, which is used to respond to the event or condition.

Why talk about callback functions? Because we talked about function pointers and so on, and callback functions are functions for function pointers and functions called through function pointers. How to use them?

We can open MSDN and check qsort function

 

  We can find that it is a sort function by name. The return value is vid. There are many parameters in it, except the previous void* base and size_t num and width, let's see, is it a function pointer? Therefore, we find that function pointers are very useful and powerful.

Since qsort is a sort function, how to sort? How to call the function pointer? Let's look at the code:

void qsort(void* base, 	//void * - pointer without concrete type
							//Can receive any type of pointer
							//Disadvantages: no operation, no addition or subtraction of integers, no dereference
			size_t num,//Number of elements to be sorted 
			size_t width,//The size of an element in bytes 
			int(__cdecl* compare)(const void* elem1, const void* elem2));//cmp refers to the function used to compare the size of two elements when sorting
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void print(int arr[], int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);
	return 0;
}

The first base in qsort is the starting address of the array to be queued, that is, arr, followed by size_ The num and width defined by t represent the number of elements in the generation array, and the other width means width, that is, the size of each element to be arranged (size_t==unsigned int). The last function pointer is used to compare the size of the two passed in elements. How can it be compared? Through the subtraction of two elements, if it is greater than zero, it means that the first element is greater than the second element. If it is less than zero, it means otherwise. We can see that some return values and parameter types are represented by void *. Why? Is there still space for this data type? In fact, this means that any type can be passed in or returned. There is no type restriction on the data to be sorted. Void * can be converted to any type, int * can be char * can be float *. I don't need to say more about the role of const. The parameters passed in the comparison function cannot be changed at will, otherwise the sorting accuracy will be affected. And CMP_ The operation that int passes into the qsort function is called a callback function.

typedef struct Stu 
{
	char name[20];
	int age;
}s;
int cmp_by_name(const void* e1, const void* e2)
{
	return strcmp(((s*)e1)->name, ((s*)e1)->name);
}
int cmp_by_age(const void* e1, const void* e2)
{
	return ((s*)e1)->age - ((s*)e2)->age;
}
int main()
{
	s arr[]= { {"Zhang San",20}, {"Li Si",10}, {"Wang Wu",30} };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//Sort by name
	qsort(arr, sz, sizeof(arr[0]), cmp_by_name);
	//Sort by age
	qsort(arr, sz, sizeof(arr[0]), cmp_by_age);
	return 0;
}

  If we define a structure and shoot relative to the data in the structure, we can also use qsort. Of course, the comparison method is still subtraction. If it is Chinese characters, the subtraction is converted into binary code values. You can get your own compiler and input several data to test.

Our bubble sorting can also be realized by callback function:

void Swap(char* buf1, char* buf2, int width)
{
	for (size_t i = 0; i < width; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}
void BubbleSort(void* base, size_t num, size_t width, int(*cmp)(const void* e1, const void* e2))
{
	for (size_t i = 0; i < num-1; i++)
	{
		for (size_t j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0)
			{
				//exchange
				Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);
			}
		}
	}
}
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
void print(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	BubbleSort(arr, sz, sizeof(arr[0]), cmp_int);
	print(arr, sz);
	return 0;
}

We'd better encapsulate a BubbleSort function first. The parameters passed in are roughly the same as the array sorting just now, but we need to not only judge the size, but also exchange them. Here we see that the parameter type of cmp call in if judgment is actually char *. In fact, we don't need to be surprised. It is also multiplied by a width, That is, it is still equal to the size of an int type. Other functions are similar to ordinary bubble sorting.

9. Analysis of pointer and array interview questions

After talking so much, we still need to implement the questions. Let's take a look at some interview questions about arrays and pointers. I believe we will have a good grasp of these questions. (I have written the analysis of the topic in the back. If you don't understand it, you can read the relevant contents in the front. I won't repeat it here.)

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//16
	printf("%d\n", sizeof(a+0));//4 / 8 A indicates the first element address, and a+0 is also the first element address
	printf("%d\n", sizeof(*a));//4 a indicates the address of the first element, * a is the first element - > a [0] * a = = * (a + 0) = = a [0]
	printf("%d\n", sizeof(a+1));//4 / 8 A represents the address of the first element, and a+1 represents the address of the second element
	printf("%d\n", sizeof(a[1]));//4
	printf("%d\n", sizeof(&a));//4 / 8 &a - fetch the address of the entire array
	printf("%d\n", sizeof(*&a));//16 &a - take out the address of the entire array, and get the array by logarithmic group dereference. The size is 16 * & A = = a
	printf("%d\n", sizeof(&a+1));//4/8 	 Skip the entire array, but still the address
	printf("%d\n", sizeof(&a[0]));//4/8	
	printf("%d\n", sizeof(&a[0]+1));//4/8	
	return 0;
}
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//6
	printf("%d\n", sizeof(arr + 0));//four 	 Arr represents the first element address, and arr+0 is also the first element address
	printf("%d\n", sizeof(*arr));//1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4/8
	printf("%d\n", sizeof(&arr + 1));//4/8
	printf("%d\n", sizeof(&arr[0] + 1));//4/8
	return 0;
}
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));//Random value, not initialized '\ 0'
	printf("%d\n", strlen(arr + 0));//Random value
	printf("%d\n", strlen(*arr));//*arr - 'a' - 97 illegal access strlen thinks that the ascii code value 97 of 'a' passed in is the address - error
	printf("%d\n", strlen(arr[1]));//ditto
	printf("%d\n", strlen(&arr));//Random value implicit type conversion
	printf("%d\n", strlen(&arr + 1));//The random value is 6 less than the first random value
	printf("%d\n", strlen(&arr[0] + 1));//Random value
	return 0;
}
int main()
{
	char arr[] = "abcdef";//a b c d e f \0
	printf("%d\n", sizeof(arr));//7
	printf("%d\n", sizeof(arr + 0));//4/8 	 arr is the first element address
	printf("%d\n", sizeof(*arr));//1	'a'
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4/8
	printf("%d\n", sizeof(&arr + 1));//4/8
	printf("%d\n", sizeof(&arr[0] + 1));//4/8
	return 0;
}
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//6
	printf("%d\n", strlen(*arr));//*arr - 'a' - 97 illegal access strlen thinks that the ascii code value 97 of 'a' passed in is the address - error
	printf("%d\n", strlen(arr[1]));//Arr [1] - [b '- 98 illegal access strlen thinks that the ascii code value 98 of' B 'passed in is the address - error
	printf("%d\n", strlen(&arr));//6
	printf("%d\n", strlen(&arr + 1));//Random value
	printf("%d\n", strlen(&arr[0] + 1));//5
	return 0;
}
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));//4/8
	printf("%d\n", sizeof(p + 1));//4/8
	printf("%d\n", sizeof(*p));//1
	printf("%d\n", sizeof(p[0]));//1
	printf("%d\n", sizeof(&p));//4/8 char**p
	printf("%d\n", sizeof(&p + 1));//4/8
	printf("%d\n", sizeof(&p[0] + 1));//4/8
	return 0;
}
int main()
{
	const char* p = "abcdef";
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//5
	printf("%d\n", strlen(*p));//error
	printf("%d\n", strlen(p[0]));//error
	printf("%d\n", strlen(&p));//Random value
	printf("%d\n", strlen(&p + 1));//Random value
	printf("%d\n", strlen(&p[0] + 1));//5
	return 0;
}
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));//48
	printf("%d\n", sizeof(a[0][0]));//4
	printf("%d\n", sizeof(a[0]));//16
	printf("%d\n", sizeof(a[0] + 1));//4/8 	 The entire array is only counted when it is placed inside sizeof alone
	printf("%d\n", sizeof(*(a[0] + 1)));//4/8
	printf("%d\n", sizeof(a + 1));//sixteen 	 A is not placed in sizeof alone, and there is no &, so a represents the address of the first element (the first line), so a+1 is the address of the second line
	printf("%d\n", sizeof(*(a + 1)));//16	
	printf("%d\n", sizeof(&a[0] + 1));//sixteen 	 Address of the second line
	printf("%d\n", sizeof(*(&a[0] + 1)));//16	*(&a[0]+1) -> *(&a[1]) -> a[1]
	printf("%d\n", sizeof(*a));//16
	printf("%d\n", sizeof(a[3]));//sixteen 	 sizeof does not participate in the internal operation and will not be accessed. Assuming that a[3] exists, it only calculates the size of the type attribute (one-dimensional array)
	return 0;
}
int main()
{
	int a[] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(ptr - 1));//2,5	
	return 0;
}
struct Test
{
	int Num;
	char* pcName;
	short sData;
	char cha[2];
	short sBa[4];
}* p;//The size is 20 bytes
int main()
{
	p = (struct Test*)0x00100000;
	printf("%p\n", p + 0x1);//0x00100014
	printf("%p\n", (unsigned long)p + 0x1);//0x00100001
	printf("%p\n", (unsigned int*)p + 0x1);//0x00100004
	return 0;
}
int main()
{
	int a[] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);//4
	int* ptr2 = (int*)((int)a + 1);//2000000
	printf("%x,%x", ptr1[-1], *ptr2);//ptr[-1] == *(ptr-1)
	return 0;
}
int main()
{
	int a[3][2] = { (0,1),(2,3),(4,5) };//Comma expression! TMD!
	int* p;
	p = a[0];
	printf("%d", p[0]);//1
	return 0;
}
int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[3][2]);
	return 0;
}
int main()
{
	int aa[2][5] = { 1,2,3,4,5,6,8,7,9,10 };
	int* ptr1 = (int*)(&aa + 1);
	int* ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
	return 0;
}
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);//at
	return 0;
}
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };//F P N E
	char*** cpp = cp;
	printf("%s\n", **++cpp);//POINT 	 Self increment changes its own value, which will be retained in the first printf statement
	printf("%s\n", *-- * ++cpp + 3);//ER
	printf("%s\n", *cpp[-2] + 3);//ST
	printf("%s\n", cpp[-1][-1] + 1);//EW
	return 0;
}

Well, let's talk about the advanced pointer. I believe it will take some time to master all these contents, so come on, let's see you next time.

Topics: C R Language data structure