[C language] advanced pointer

1. Character pointer

What is the output of the following code?

Answer: h hello the hello the

Analysis: character pointer variables can store strings.

The first h is because the * ps pointer points to the first character h of hello, so h is output; The last two array names, arr and ps, are the first character address of hello, so they both output hello.

int main()
{
	//In essence, the first character address of the string "hello the" is stored in ps
	char* ps = "hello the";
	char arr[] = "hello the";
	
	printf("%c\n", *ps);//h
	
	printf("%s\n", ps);
	printf("%s\n", arr);
	return 0;
}

What is the output of the following code?

Answer: 1 and 2 are different, 3 and 4 are the same

Analysis: the array name represents the address of the first element of the array. str1 and str2 are two different arrays, which need to open up two different memory spaces respectively. str1 and str2 point to the first addresses of two hello, so they are different;

"hello bit" is a constant string, and the pointer variable cannot be changed by dereference. For memory, since "hello bit" is a constant, you only need to open up a memory space for it, and the address number is determined. Therefore, 3 and 4 point to the same address, that is, the first element address of the constant string "hello bit"

int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char* str3 = "hello bit.";
    const 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;
}

2. Pointer array

Definition: pointer array is an array for storing pointers. In essence, it is an array in which pointers are stored

In the following code: three integer pointers int * can be stored in the ARR array, and a, B and C are the addresses of the first elements of the three integer arrays respectively, in which arr [0], arr [1] and arr [2]

int main()
{
	//Pointer array
	//Array - the array contains pointers (addresses)
	//int* arr[3];// An array that holds integer pointers
	int a[] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };
	int* arr[3] = { a,b,c };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", *(arr[i]+j));
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

 3. Array pointer

Definition: it is a pointer that should be able to point to the array. The array pointer should store the address of the array

Next, it will be different from the previous pointer array:

  • int *p1[10];—— Pointer array

The priority of [] is higher than that of *, so () must be added to ensure that p is combined with * first

  • int (*p2)[10];

P is first combined with * to indicate that P is a pointer variable, and then points to an array of 10 integers. So p is a pointer to an array, called an array pointer

Think about the form of array pointers

First, the pointer (*) → how many elements are there in the array pointed to by the pointer ([]) → what type of array is pointed to

int main()
{
	int arr[10] = { 1,2,3,4,5 };
	//Arr -- is the address of arr[0]
	int(*parr)[10] = &arr;//The array address is taken out
	//parr is an array pointer -- where the address of the array is stored
	
	double* p[10];
	double* (*pd)[10] = &p;
	return 0;
}

What is the connotation

Observe the following codes:

int main()
{
	int arr[10] = {1,2,3,4,5,6,7,8,9,10 };
	int(*pa)[10] = &arr;//Array pointer
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d\n", (*pa + i));
		printf("%d\n", *(*pa + i));
	}
	return 0;
}

After running, let's take a look:

  1. *Note that pa is a pointer variable. pa should store the address of an array, and pa+1 will skip the address size of an array;
  2. *pa is the address of the first element, that is, the name of the arr array, * pa+1, that is, the address advances by 4 bytes (matching the int pointer type), reflecting the address of each element in the array
  3. Dereference operation: * (* pa) get the elements in the arr array

Practical application: print each element in a two-dimensional array

p is the array pointer, pointing to an array, * p saves the address of the first element (arr array name), [5] indicates that there are 5 elements in the array, and int indicates the array element type

The first element of the two-dimensional array is the first row of the two-dimensional array. The arr passed here is actually equivalent to the address of the first row, which is the address of the one-dimensional array

  • (p+i) get the address of each row array
  • *(p+i) get the address of the first element of each row array
  • (* (p+i) + j) get the address of each element in each row array
  • *(* (p+i) + j) get the element array contents in each row array
void print(int(*p)[5],int r,int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%p ", (p + i) );
			printf("%p ", *(p + i) );
			printf("%p ", *(p + i)+j);
			printf("%d ", *((*(p+i) + j)));
		}
		printf("\n");
	}
}



int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr,3,5);
	return 0;
}

Verify the following results:

Judge the meaning of the following code

int arr[5];—— An array with five integer elements

int *parr1[10];—— An array of 10 integer pointers

int (*parr2)[10];—— The pointer can point to an array, 10 elements in an array, and each element type is int

int (*parr3[10])[5];—— Parr3 is an array that stores array pointers. The array can store 10 pointers. Each array pointer can point to an array with 5 elements. Each element is of type int

4. Array parameters and pointer parameters

Be sure to analyze clearly what is passed by the argument. If it is an address, whose address is it, and in what form should the formal parameter be received.

One dimensional array parameter transfer

Judge which parameters can be transferred and which cannot?

#include <stdio.h>
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
 int arr[10] = {0}
 int *arr2[20] = {0};
 test(arr);
 test2(arr2);
}

The form of formal parameters here is correct.

  • test()

1,2: receive as array √ correct

3: arr is the array name. At this time, the first element address is passed. It is correct to receive √ with int *

  • test2() -- int* arr2[20] is a pointer array that stores pointers of type int *

4: Pass the pointer array, and the corresponding type receives √ correctly

5: arr2 is the address of the first element of the pointer array. The first element is a pointer of type int *, which becomes a secondary pointer √ correct

Two dimensional array parameter transfer

Judge which parameters can be transferred and which cannot?

void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?


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);
}
  • arr is a two-dimensional array. A two-dimensional array may not know the number of rows, but it must know the number of columns, that is, how many elements there are in a row

So 1 √ correct, 2 × Error, 3 √ correct

  • The address of the first element of arr represents the address of the first line element. It is understood as an array pointer and should point to an array

1: It is understood that arr is an array pointer (it can point to the array in the first row, and the first row is an array with 5 integers). The corresponding formal parameter is malformed, but it is only a first-order pointer × error

2: The parameter received should be a pointer array × error

3: arr is an array pointer. There are 5 int type elements in the array pointed to √ correct

4: What passed is not a secondary pointer × error

Primary pointer transfer parameter

The form of function parameter is that when receiving the first level pointer, the actual parameter passes the address

Secondary pointer transfer parameter

The function parameter form is that when receiving the secondary pointer, the argument can pass the secondary pointer, that is, the address of the primary pointer, or the array name storing the primary pointer

void test(char **p)
{
 
}
int main()
{
 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);
 test(ppc);
 test(arr);//Ok?
 return 0;
}

Analysis:

pc is the primary pointer, ppc is the secondary pointer, and test(ppc) passes the secondary pointer ppc as a parameter;

Test (& PC) pass the address of the first level pointer variable;

test(arr) passes the array storing the first level pointer and the name of the arr array. The array name represents the address of the first element. Each element of the arr array is of type char * and the address of char * is equivalent to char * *, which meets the receiving requirements of formal parameters in char(int ** p)

5. Function pointer

Definition: pointer to function and pointer to store function address

Form of function pointer:

                                                    int(*pf)(int,int)

First, it should be a pointer (for example, * pf), pointing to a function. What is the type of function?

The function includes the type of function parameter and the return type of the function. Therefore, the function pointer should also indicate the components of the pointed function.

//Function pointer - a pointer that stores the address of a function
//&Function name - what you get is the address of the function
int Add(int x, int y)
{
	return x + y;
}
int main()
{
	//&The function name is equivalent to the function name
	printf("%p\n",&(Add));
	printf("%p\n",Add);

	//Function pointer variable
	/*int(*pf)(int, int) = &Add;*/
	int(*pf)(int, int) = Add;//Means Add===pf
	printf("%d\n", (*pf)(3, 5));
	printf("%d\n", pf(3, 5));
	printf("%d\n", Add(3, 5));
	return 0;
}

Observe the result of the above code:

  1. Function name = = & function name, indicating that the function name is the address of the function! (also remember array name! = & array name)
  2. In the past, we used to call the function in the form of Add(3,5) with the same effect as int (*pf) (3,5)

pf is the function pointer variable, which stores the address of the function. From this, we can know that pf==Add, and the function can also be written as pf(3,5)

(* is the understanding of form, but it doesn't work in practice)

6. Function pointer array

As the name suggests - an array for storing function pointers. The address of the function is stored in an array, and the function pointers of the same type are stored at the same time

int (*pf)(int,int)=Add;
int (*pf1)(int,int)=Sub;
int (*pfArr[2])(int,int);//Function pointer array pfArr

Design a calculator to understand:

//Function pointer array
//Suppose you implement a calculator that calculates integer variables and can add, subtract, multiply and divide
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("*************  1.Add  ***************\n");
	printf("*************  2.Sub  ***************\n");
	printf("*************  3.Mul  ***************\n");
	printf("*************  4.Div  ***************\n");
	printf("*************  0.sign out ***************\n");
}
int main()
{
	int input = 0;
	do
	{
		int(*pfArr[5])(int, int) = {NULL,Add,Sub,Mul,Div};
		int x = 0;
		int y = 0;
		menu();//Calculator interface
		printf("Please do what you need:>");
		scanf("%d", &input);
		
		if (input >= 1 && input <= 4)
		{
			printf("Please carry out the two values of the calculation you need:>");
			scanf("%d%d", &x, &y);
			printf("%d\n", pfArr[input](x, y));
		}
		else if (input == 0)
		{
			printf("Exit program\n");
			break;
		}
		else
		{
			printf("Please reselect\n");
		}
		
	} while(input);
	return 0;
}

7. Pointer to function pointer array

//integer array 
int arr[5];
int (*p1)[5]=&arr;
//p1 is a pointer to (integer array)


//Array of integer pointers
int* arr[5];
int* (*p2) [5]=&arr;
//p2 is a pointer to (an array of integer pointers)

//Function pointer
int (*p)(int,int);
//Function pointer array
int (*p2[4])(int,int);
p3=&p2;//The address of the function pointer array is taken out
//p3 is a pointer to (array of function pointers)
int (*(*p3)[4])(int,int);

8. Callback function

A callback function is a function called through a 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.

Quick understanding: a function itself is not directly used, and its address is called as a formal parameter of another function

Example 1 -- implementation of calculator

//Callback function
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 Calc(int (*pf)(int, int))
{
	int x = 0;
	int y = 0;
	printf("Please enter 2 operands>:");
	scanf("%d %d", &x, &y);
	return pf(x, y);
}

int main()
{
	int input = 0;
	//Calculator - calculates the addition, subtraction, multiplication, and division of integer variables
	//a&b a^b a|b a>>b a<<b a>b

	do {
		menu();

		int ret = 0;
		printf("Please select:>");
		scanf("%d", &input);

		switch (input)
		{
		case 1:
			ret = Calc(Add);
			printf("ret = %d\n", ret);
			break;
		case 2:
			ret = Calc(Sub);
			printf("ret = %d\n", ret);
			break;
		case 3:
			ret = Calc(Mul);//
			printf("ret = %d\n", ret);
			break;
		case 4:
			ret = Calc(Div);//
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("Exit program\n");
			break;
		default:
			printf("Selection error, reselect!\n");
			break;
		}

	} while (input);
	return 0;
}

Example 2 -- learning and Simulation Implementation of qsort function

The qsort function can arrange elements of any type, including integer variables, string variables, structures, and so on

First, let's learn the connotation of qsort function

base The first address of the first element of the array to be sorted is stored in

num Is the number of elements in the data array to be sorted

size The size of an array element in bytes

int (*compar)(const void*,const void*)A function that compares two elements in the data to be sorted

Use:

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

void test1()
{
	//Sorting of integer data
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//sort
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	//Print
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}

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

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

int sort_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct Stu*)e1)->name,((struct Stu*)e2)->name);
}

void test2()
{
	//Sorting structure data using qsort function
	struct Stu s[] = { {"zhangsan",30},{"lisi",40},{"wangwu",20}};
	int sz = sizeof(s) / sizeof(s[0]);
	
	//Sort by age
	/*qsort(s, sz, sizeof(s[0]), sort_by_age);*/
	
	//Sort by name
	qsort(s, sz, sizeof(s[0]), sort_by_name);
}

int main()
{
	//test1();
	test2();
	return 0;
}

Simulation Implementation

//Imitate qsort function to realize a general algorithm for bubble sorting
//The comparison method of different types of data shall be determined by the user
void Swap(char* buf1, char* buf2,int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *buf1;
		*buf1 = *buf2;
		*buf2 = tmp;
		buf1++;
		buf2++;
	}
}


void bubble_sort(void* base, int num, int size,
	int(*cmp)(const void* e1, const void* e2))
{
	int i = 0;
	//Lying number
	for (i = 0; i <num-1; i++)
	{
		//Sorting of a trip
		int j = 0;
		for (j = 0;j < num - 1 - i; j++)
		{
			//Comparison of two elements
			if (cmp((char*)base+j*size,(char*)base+(j+1)*size) > 0)
			{
				//exchange
				Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}
}

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

void test3()
{
	//Sorting of integer data
	int arr[] = { 9,8,7,6,5,4,3,2,1 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//sort
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	//Print
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}


int main()
{
	test3();
	return 0;
}

Points to note:

1. Role of byte size

As the designer of qsort function, byte size is very important because we don't know what type of data the user is comparing;

2. How to get the address when comparing the two data? Why cast a pointer of type char *

Because the user will tell the difference between the previous element and the latter element by several bytes through the formal parameter size. The pointer of char * type is a byte, so skip one element: (char *) address + 1*size, and skip two elements: (char *) address + 2*size;

 3. How to implement the internal exchange function? Do not know what type of element, how to exchange the contents of the two elements?

Because we don't know the type of element, we can't convert the pointer to the corresponding type for exchange, but we know the byte size of an element. Therefore, we still pass the pointer of char * type through the exchange of size byte by byte

 4. Think: why use void * empty finger needle

Because the void*p null pointer can store any type of pointer, the qsort function does not know what type of array needs to be sorted, so it is received with void * first, and the specific type is informed by the user.

Keywords: C Back-end

Added by legacyblade on Sat, 22 Jan 2022 07:59:21 +0200