Pointer --- Advanced

Look at the catalog first to see how much you know

Table of contents

1. Character pointer

2. Array of pointers

3. Array pointer

4. Array parameter passing and pointer parameter passing

5. Function pointer

6. Array of function pointers

7. Pointer to array of function pointers

8. Callback function


What is a pointer, we have already touched it in the previous "Pointer" chapter, we know the concept of a pointer:

1. A pointer is a variable used to store an address, which uniquely identifies a piece of memory space.

2 The size of the pointer is fixed at 4/8 bytes (32-bit platform/64-bit platform).

3. The pointer has a type, and the type of the pointer determines the step size of the pointer +- integer, and the authority of the pointer dereference operation.

4. Pointer operations. The size can be compared. If two pointers point to the same array, they can also be subtracted. The absolute value of the obtained number is the number of elements between them.

1. Character pointer

In the pointer type, we know that there is a pointer type that is a character pointer char* ;

General usage:

int main()
{
	char ch = 'w';
	char* pc = &ch;//pc就是字符指针,指向字符的指针就是字符指针
    *pc = 'w';
	return 0;
}

Another use case is as follows: store the address of the first character of the constant string

#include<stdio.h>

int main()
{
	//下面右边是一个表达式,表达式的值是首字符的地址
	char* p = "abcdef";//是把首字符的地址放到p中  
	//*p = 'w';//错误,这里字符串是常量字符串,不能修改  
	//const char *p = "abcdef";//这样写会更加严谨,
	
	char arr[] = "abcdef"; //可以想象为字符串是arr,p是存放的arr的首元素地址
	char* parr = arr;     // p指向的是arr首元素地址,不同的是arr数组是可以修改
	*parr='w';//可以修改arr字符串的值
	printf("%s\n", arr);

	return 0;
}

The code  char* p = "abcdef";  is particularly easy to make us think that the string abcdef is put into the character pointer p, but the / essence is to put the address of the first character of the constant string abcedf into p, which is the address of a Put it in the pointer variable p.

look at this question

#include <stdio.h>

int main()
{
	char str1[] = "hello xilanhua";
	char str2[] = "hello xilanhua";

	const char* str3 = "hello xilanhua";
	const char* str4 = "hello xilanhua";

	if (str1 == str2)
	{
		printf("str1 和 str2 相等\n");
	}
	else
	{
		printf("str1 和 str2 不相等\n");
	}
	if (str3 == str4)
	{
		printf("str3 和 str4 相等\n");
	}
	else
	{
		printf("str3 和 str4 不相等\n");
	}

	return 0;
}

str1 and str2 are the addresses of the first elements of the two character arrays, which open up different spaces in the memory, so str1 and str2 are not equal,

str3 and str4 store the address of the first character of the constant string "hello xilanhua". The constant string cannot be modified, so there is no need to save two copies in the memory , only need to open up a space, the space pointed to by both pointers, So both addresses are the same.

So the output is

2. Array of pointers

By analogy:

An array of integers, an array of integers

character array, an array of characters

Array of pointers, an array of pointers.

int* arr1[5];//整形指针数组
char* arr2[5];//一级字符指针的数组
char** arr3[5];//二级字符指针的数组

According to the character pointer learned above, let's look at the following code:

#include <stdio.h>

int main()
{
	char* arr[] = { "abcdef","hehe","xilanhua" };//字符指针数组
	//               arr0	   arr1		arr2    //这里存放的是常量字符串的首字符的地址

	//*arr[1] = 'w';//错误,这里数组元素指向的也是常量字符串,不能修改

	for (int i = 0; i < 3; i++)
	{
		printf("%s\n", arr[i]);
	}

	return 0;
}

result

 Look at this code:

#include<stdio.h>

int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//数组名是首元素地址
	//arr是存放整形指针的数组    指针数组
	int* arr[] = { arr1,arr2,arr3 };
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)
	{
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);//和二维数组很像
			//printf("%d ", *(arr[i] + j));
            //*(arr[i]+j)==arr[i][j]     arr[i]==*(arr+i)
		}
		printf("\n");
	}

	return 0;
}

result:

It can be found that it is very similar to a two-dimensional array, but it is different in essence. A two-dimensional array is a continuous storage space, but it is not here, it is connected through pointers.

3. Array pointer

Also by analogy:

Integer pointer, pointing to an integer pointer variable

pointer to character, pointer variable to character

array pointer, pointer variable pointing to an array

Which of the following is an array pointer?

int* p1[10];

int (*p2)[10];

explain:

int (*p)[10]

p is first combined with *, indicating that p is a pointer variable, and then it points to an array of 10 integers. So p is a pointer pointing to an array, called an array pointer.
Note here: [ ] has a higher priority than *, so () must be added to ensure that p is combined with * first. Without (), it is an array of pointers.

1. & array name and array name

Look at the code below:

#include<stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);//数组名是首元素地址 两个例外 &数组名和sizeof(数组名)
	printf("%p\n", &arr[0]);//首元素地址
	
	printf("%p\n", &arr);  //三个数值上相等

	printf("%p\n", arr + 1);//int* + 1
	printf("%p\n", &arr[0] + 1);//int* + 1
	printf("%p\n", &arr + 1);//加了40 int(*)[10]+1
	int(*p)[10] = &arr;
	//int(*)[10]  这个数组指针的类型

	return 0;
}

According to the above code, we found that, in fact, &ar r and arr have the same value, but their meanings are different. In fact: &arr represents the address of the array, not the address of the first element of the array. (Experience in detail). The address of the array is +1, skipping the size of the entire array, so the difference between &arr+1 and &arr is 40.

Summarize:

 The array name is the address of the first element, there are two exceptions & the array name and sizeof (array name)
1. sizeof(arr) - When an array name is placed inside sizeof, the array name represents the entire array, and the calculated size is the size of the array
2. &arr - The array name here represents the entire array, and the address of the entire array is taken out. From the perspective of the address value, it is the same as the address of the first element of the array, but the meaning is different. 

2. Different ways of accessing array elements

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	
	//下标形式访问数组
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}


	//使用指针访问数组
	int* p = arr;
	for (i = 0; i < sz; i++)
	{
		for (i = 0; i < sz; i++)
		{
			printf("%d ", *(p + i)); 
		}
	}

	//数组指针访问数组   虽然对,但是不推荐
	int(*pa)[10] = &arr; 
	//pa == &arr
	//*pa == *&arr
	//*pa == arr 数组名 不是两种特殊情况 是首元素地址
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *((*pa)+i));//(*pa)[i];
	}

	return 0;
}

Notice:

int(*pa)[10] = &arr; 
    //pa == &arr
    //*pa == *&arr
    //*pa == arr The array name is not two special cases, but the address of the first element

So: Array pointer dereference is the address of the first element of the array

3. Use of array pointers

The parameter passing of the two-dimensional array should use the pointer array.

#include<stdio.h>

//普通接收二维数组传参形式
void print1(int arr[3][5], int r, int c)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

//二维数组传参用到数组指针
void print(int (*arr)[5], int r, int c)//指针形式接受  数组指针
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)//arr+i是每一行  *(arr+i)是每一行的数组名 arr[i] 首元素地址就是每一行的第一个元素的地址
	{
		for (j = 0; j < c; j++)
		{
			//printf("%d ", *(*(arr + i) + j));//解引用数组指针就是数组首元素地址,二维数组是第一行地址
			printf("%d ", arr[i][j]);//*(*(arr+i)+j)==*(arr+i)[j]==arr[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;
}

The array name of the two-dimensional array, which also indicates the address of the first element. The address of the first element of the two-dimensional array is the address of the first row, which stores a one-dimensional array. It can be understood that a two-dimensional array is an array that stores a one-dimensional array

4. Array parameter passing and pointer parameter passing

When writing code, it is inevitable to pass [array] or [pointer] to the function. How should the parameters of the function be designed?

One-dimensional array parameter passing:

The formal parameter can be an array or a pointer. When the formal parameter is a pointer, pay attention to the type.

#include<stdio.h>

void test(int arr[])//不会真的创建一个数组,不写大小
{}
void test(int arr[10])//和传入格式一样,不会真的创建
{}
void test(int* arr)//数组名,首元素地址,指针接受
{}
void test2(int* arr[20])//指针数组,和传入格式一样
{}
void test2(int** arr )//数组首元素是int* 类型,*arr说明他是指针
{}

int main()
{
	int arr[10] = { 0 };//整形数组
	int* arr2[20] = { 0 };//整形指针数组

	test(arr);

	test2(arr2);
	return 0;
}

Two-dimensional array parameter passing:

The parameter can be a pointer or an array. If it is an array, the row can be omitted, but the column cannot be omitted. If it is a pointer, the parameter passed is the pointer of the first row, and the formal parameter should be an array pointer

void test(int arr[3][5])
{}
//void test(int arr[][])//错误,只能省略行,因为得知道一行有几个元素
//{}
void test(int arr[][5])
{}
//void test(int *arr)//错误,二维数组的首元素地址是第一行的地址
//{}
//void test(int* arr[5])//错误,这是一个指针数组,存放整形指针的数组
//{}
void test(int(*arr)[5])//数组指针,可以接收实参
{}
//void test(int** arr)//错误,二级指针
//{}

int main()
{
	int arr[3][5] = { 0 };
	test(arr);
	return 0;
}

First-level pointer parameter passing:

#include<stdio.h>
void print(int* p, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d\n", *(p + i));//通过指针访问
	}
}

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//一级指针
	int sz = sizeof(arr) / sizeof(arr[0]);//元素个数
	print(p, sz);
	return 0;
}

Thinking: When a function parameter is a pointer, what parameters can the function receive

void print(int* p);    
1.int a;
  print(&a);// address of variable
2.int* p=&a;
  print(p);// pointer variable
3.int arr[10];
  print( arr);// array name

Secondary pointer parameter passing:

#include<stdio.h>

//用二级指针接收
void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}

int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	test(pp);
	test(&p);
	return 0;
}

Thinking: When the parameter of the function is a secondary pointer, what parameters can be received

1. The address of the first-level pointer

2. Secondary pointer variable,

3. Array of pointers

5. Function pointer

Here's another analogy:

integer pointer, pointer to integer int*

character pointer, pointer to character char*

array pointer, pointer to array, int arr[10]; int (*p)[10] = &arr;

Function pointer, a pointer to a function, that is, the address where the function is stored

So the question is, does the function have an address?

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
//&函数名得到就是函数的地址
int main()
{
	printf("%p\n", &Add);//可以运行
	printf("%p\n", Add);//可以运行

	return 0;
}
//函数名 和 &函数名都是函数的地址

result: 

 It can be concluded that both function name and & function name are addresses of functions .

Call a function using a function pointer:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//()是pf先于*结合,说明pf是指针
	int (*pf)(int, int) = Add;//函数的地址要存起来,就得放在 函数指针变量pf 中
	//最前面的是函数返回类型,后面括号内是函数参数类型

	//通过函数指针调用函数
	int ret = (*pf)(3, 5);
	ret = Add(3, 5);//函数可以这样调用,Add是地址
	ret = pf(3, 5);//所以这样写也对
	ret = (*****pf)(3, 5);//编译器在处理时,会把*去掉,也没有问题
	printf("%d\n", ret);//8

	return 0;
}

You can read two interesting pieces of code

1.(*(void(*)( ))0)( );

#include<stdio.h>

int main()
{
	//1.将0强制类型转换为void(*)()类型的函数指针
	//2.这就意味着0地址处放着一个函数,函数无参,返回类型是void
	//3.调用0地址处的这个函数
	(*(void(*)())0)();//前面*可以不写

	return 0;
}

2.void(* signal(int, void(*)(int) ) )(int);

int main()
{
	void(* signal(int, void(*)(int) ) )(int);
	//signal先于()结合
	//void(*)(int)
	//signal(int, void(*)(int));//函数名称和函数参数类型
	//上述代码是一个函数的声明,
	//函数的名字是signal
	//函数的参数第一个是int类型,第二个是void(*)(int)类型的函数指针
	//该函数指针指向的函数参数是int,返回类型是void
	//signal函数的返回类型也是一个函数指针
	//该函数指针指向的函数参数是int,返回类型是void
	return 0;
}

可以通过类型重命名简化为:
//typedef int(*)(int) pt_f;//写法不对
typedef void(*pf_t)(int);//将void(*)(int)类型的函数指针重命名叫pf_t 

pf_t(signal(int, pf_t));

6. Array of function pointers

array of function pointers

How to write an array of function pointers

函数指针
int (*p)(int,int);

函数指针数组只需在p后加一个[大小],让指p先于[]结合
int (*p[10])(int,int);
//数组存放元素的类型就是 int (*) (int,int)

 Purpose of array of function pointers: transfer table

Example: (calculator)

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

void menu()//菜单函数
{
	printf("************************\n");
	printf("**** 1.add    2.sub ****\n");
	printf("**** 3.mul    4.div ****\n");
	printf("****     0.exit     ****\n");
}

int main()
{
	//转移表 - 函数指针数组   函数指针类型要相同
	int(*pfArr[5])(int, int) = { NULL,Add,Sub,Mul,Div };//NULL==0
	//						下标 0  1  2    3   4
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请输入:> ");
		scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器\n");
			break;
		}
		if (input <= 4 && input >= 1)
		{
			printf("请输入两个操作数:>");
			scanf("%d %d", &x, &y);
			printf("%d\n",pfArr[input](x, y));//调用相应的计算函数
		}
		else
		{
			printf("输入非法,请重新输入\n");
		}
		
	} while (input);
	return 0;
}

7. Pointer to array of function pointers

A pointer to an array of function pointers is a pointer, and the pointer points to an array whose elements are function pointers .

 Example:

#include<stdio.h>

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}

int main()
{
    //函数指针
	int(*pf)(int, int) = Add;
	//函数指针数组
	int(*pfArr[4])(int, int) = { Add,Sub };

	//ppfArr是一个指向一个函数指针数组的指针变量
	int(*(*ppfArr)[4])(int,int) = &pfArr;

	return 0;
}

8. Callback function

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.

Here is a library function that uses a callback function, the qsort function , including the header file stdlib.h.

We can find the usage of qsort function on the old version of c++ official website qsort usage

 First look at the return value type and parameter list

 It can sort any type of data, but you need to write a function to determine the size of the two elements, which is the compare function in the parameter list.

void qsort( void* base,//待排序数组的第一个元素
			size_t num, //待排序的元素个数
			size_t size, //每个元素的大小
			int(*cmp)(const void*, const void*));//函数指针,指向一个函数,这个函数可以比较2个元素的大小

The bottom layer of the qsort function uses quick sort, which is also a sorting algorithm. Here we use bubble sort to implement it.

Because this function can sort any type, we initially use a void * pointer to receive the passed pointer, and then use the mandatory type conversion (char*) plus size to determine the size of the operation space.

Let's first implement the following cmp function

This is the requirement of the cmp function, and the return value has three situations: greater than 0, less than 0, and equal to 0. Greater than 0 points to p1, p1 is larger, less than 0 points to p2, p2 is larger, equal return value is 0.

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
    //这是我们自己写的函数,我们知道是int类型,所以强制类型转换为(int*)
}

Swap function:

Because we don't know what type of elements to exchange, we use (char*) mandatory type conversion, exchange one byte at a time, and loop size times

void Swap(char* p1, char* p2, size_t size)
{
	for (size_t i = 0; i < size; i++)
	{
		char t = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = t;
	}
}

Sort inside:

void bubble_sort(void* base, size_t num, size_t size, int(*cmp)(const void*, const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num - 1; i++)//一趟冒泡排序
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			//两个相邻元素比较
			//arr[j]与arr[j+1]  调用我们自己写的比较函数,回调函数
			if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
			{
				//交换
				Swap((char*)base + j * size, (char*)base + (j + 1) * size,size);
			}
		}
	}

Full code:

#include<stdio.h>
#include<stdlib.h>

int cmp_int(const void* p1, const void* p2)
{
	return *(int*)p1 - *(int*)p2;
	//这是我们自己写的函数,我们知道是int类型,所以强制类型转换为(int*)
}

void Swap(char* p1, char* p2, size_t size)
{
	for (size_t i = 0; i < size; i++)
	{
		char t = *(p1 + i);
		*(p1 + i) = *(p2 + i);
		*(p2 + i) = t;
	}
}

void bubble_sort(void* base, size_t num, size_t size, int(*cmp)(const void*, const void*))
{
	size_t i = 0;
	size_t j = 0;
	for (i = 0; i < num - 1; i++)//一趟冒泡排序
	{
		for (j = 0; j < num - 1 - i; j++)
		{
			//两个相邻元素比较
			//arr[j]与arr[j+1]
			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[10] = { 1,2,8,9,10,5,3,6,4,7 };

	qsort(arr, 10, sizeof(arr[0]), cmp_int);
	
	for(int i=0;i<10;i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");

	int arr1[10] = { 1,2,8,9,10,5,3,6,4,7 };

	bubble_sort(arr1, 10, sizeof(arr[0]), cmp_int);

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

	return 0;
}

end of article

 

 

Guess you like

Origin blog.csdn.net/qq_72916130/article/details/131042866