Revisiting pointers (detailed explanation of pointer types)


foreword

Pointers are a difficult part of the C language, and you need to learn, think, learn, and think again and again. Learning pointers well can basically grasp the essence of C language data structure level.

1. Character pointer

int main()
{
    
    
	char* p = "abcde";//"abcde"是一个常量字符串
	printf("%c\n", *p);//p存放的是常量字符串的首地址
	printf("%s\n", p);
	return 0;
}

insert image description here

int main()
{
    
    
	char* p = "abcde";
	*p = 'q';//常量字符串是不可以修改的
	printf("%c\n", *p);
	printf("%s\n", p);
	return 0;
}

insert image description here
Constant strings cannot be modified. Although the program generated no errors and was generated normally, the program crashed at runtime. , we usually add const modification before the constant string .

int main()
{
    
    
	const char* p = "abcde";
	*p = 'q';//常量字符串是不可以修改的
	printf("%c\n", *p);
	printf("%s\n", p);
	return 0;
}

insert image description here
Adding const modification will prevent us from mishandling constant strings and avoiding program crashes.
Let's take a deeper look at character pointers through a case:

int main()
{
    
    
	char ch1[] = "abcde";
	char ch2[] = "abcde";
	const char* p1 = "abcde";
	const char* p2 = "abcde";
	if (ch1 == ch2)
	{
    
    
		printf("比较的是数组的元素!\n");
	}
	else
	{
    
    
		printf("比较的是数组的地址!\n");
	}
	if (p1 == p2)
	{
    
    
		printf("比较的是指针的地址!\n");
	}
	else
	{
    
    
		printf("比较的是指针的元素!\n");
	}
	if (!strcmp(ch1, ch2))
	{
    
    
		printf("比较的是数组的元素!\n");
	}
	else
	{
    
    
		printf("比较的是数组的地址!\n");
	}
	return 0;
}

insert image description here
Comparing the results, we can see that when the array name is used to compare directly , the address is compared, and the array name represents the address of the first element of the array, so the printed result is the comparison address, but the direct comparison of the pointers shows that the addresses of the two pointers are the same , because the two pointers point to the same constant string, so the two pointers point to the same allocated space . To compare the elements of two arrays, use the strcmp() function. If the results of the two arrays are the same, return 0, and if the results are different, return non-zero .

2. Array pointer

Array pointers, as the name implies, are arrays, and array pointers are used to store pointers .

int main()
{
    
    
	int arr[5] = {
    
     0 };//整形数组,里面含有5个整形元素
	char ch[5] = {
    
     0 };//字符数组,里面含有5个字符元素
	int* parr[5];//整形数组指针,里面含有5个整形指针
	char* pch[5];//字符数组指针,里面含有5个字符指针
	return 0;
}

The general usage is as follows:

int main()
{
    
    
	int arr1[] = {
    
     1,2,3 };
	int arr2[] = {
    
     4,5,6 };
	int arr3[] = {
    
     7,8,9 };
	int* parr[] = {
    
     arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", *(parr[i] + j));//j表示整形数组的元素
		}
		printf("\n");
	}
	return 0;
}

insert image description here

3. Array of pointers

An array of pointers, as the name implies, is a pointer, and an array of pointers is a pointer to an array .

int main()
{
    
    
	int* pi = NULL;//pi是整形指针,用来存放整形的地址,指向整形元素
	char* pc = NULL;//pc是字符指针,用来存放字符的地址,指向字符元素
	int arr[5] = {
    
     1,2,3,4,5 };
	int(*parr)[5] = &arr;//&arr是数组的地址,[]的优先级高与*,*和parr结合,代表parr是一个指针
	return 0;
}

The priority of [ ] is combined with *, * and parr, indicating that parr is a pointer, and then combined with [ ], indicating that it points to an array.

int main()
{
    
    
	int arr[10] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;//p指向arr数组的首元素
	for (int i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(p + i));//指针偏移i个位置
		printf("%d ", p[i]);
		printf("%d ", *(arr + i));//arr代表首元素地址,偏移i个位置
		printf("%d ", arr[i]);
		printf("\n");
	}
	return 0;
}

insert image description here
We can see that arrays can be used as pointers, and pointers can also access array elements through subscripts.
Let's take a deeper look at pointer arrays through a case:

void print1(int arr[3][3])
{
    
    
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", arr[i][j]);//j表示整形数组的元素
			//printf("%d ", *(arr[i] + j));//数组当指针用
			//printf("%d ", *(*(arr + i) + j));//数组当指针用
		}
		printf("\n");
	}
}
void print2(int(*p)[3])
{
    
    
	for (int i = 0; i < 3; i++)
	{
    
    
		for (int j = 0; j < 3; j++)
		{
    
    
			printf("%d ", *(*(p + i) + j));
			//p+i表示第几行元素,通过解引用获得这一行元素下标。
			//+j表示找到这一行的第j个元素,通过解引用获得这一个元素。
			//printf("%d ", *(p[i] + j));//指针当数组用
			//printf("%d ", p[i][j]);//指针当数组用
		}
		printf("\n");
	}
}
int main()
{
    
    
	int arr[3][3] = {
    
     {
    
    1,2,3},{
    
    4,5,6},{
    
    7,8,9} };
	int(*p)[3] = &arr;
	print1(arr);//数组首元素地址,数组形式
	printf("\n");
	print2(arr);//指针数组形式
	printf("\n");
	return 0;
}

insert image description here
What does the following mean?

int (*p[5])[10];

First, p is first combined with [ ], which means that p is an array, and p[ ] is taken out, and the structure becomes int (* ) [10].
The meaning of the above code is: the array has 5 elements, each element is a pointer array, and each pointer array points to an array containing 10 integer elements.

4. Function pointer

A function pointer is a pointer to a function.
Before understanding function pointers, let's take a look at the address of the function:

int ADD(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	printf("%p\n", &ADD);//对ADD取地址
	printf("%p\n", ADD);//函数名
	return 0;
}

insert image description here

We can find that both the function address and the function name represent the function address . So both forms of function calls have the same result.
Let's take a formal look at function pointers:

int ADD(int x, int y)
{
    
    
	return x + y;
}
int main()
{
    
    
	int* p1;//整形指针
	int* p2[5];//数组指针
	int(*p3)[5];//指针数组
	int(*p4)(int ,int);//函数指针
	p4 = &ADD;
	printf("%d\n", (*p4)(2, 5));//通过解引用调用
	printf("%d\n", p4(2, 5));//直接通过函数地址调用,和函数名调用类似
	return 0;
}

insert image description here

Comparing the pointers we have seen, we can find:

int main()
{
    
    
	int* p1;//整形指针
	//p1和*结合,代表是一个指针,类型为int
	int* p2[5];//数组指针
	//[ ]的优先级高于*,所以p2先和[ ]结合,代表p2是一个数组,剩下int *,代表存储的类型,为整形指针。
	int(*p3)[5];//指针数组
	//p3先和*结合,代表一个指针,剩下的为int [5],代表一个含有5个整形元素的数组,p3指向这个数组
	int(*p4)(int, int)=&ADD;//函数指针
	//p4先和*结合,代表一个指针,剩下的为int (int,int),代表两个形参都为int类型,返回值也为int类型
}

Using only function pointers to call functions is too cumbersome, and the usage will be mentioned in the callback function.

5. Array of function pointers

The function pointer array is an array of pointers, and the array is used to store the address of the function.
How to determine an array of function pointers?
According to the previous combination, we only need to add [ ] after the pointer name .

int(*p5[5])(int, int);//函数指针数组
//p5先和[ ]结合,代表p5是一个数组,剩下的为int(*)(int, int),这个是不是很熟悉,剩下这个就是函数指针

Let's look at the use of function pointer arrays through a simple calculator:

void test()
{
    
    
	printf("1,相加,2,相减\n");
	printf("3,相乘,4,相除\n");
	printf(" 0,退出程序\n");
}
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;
}
int main()
{
    
    
	int num = 0;
	int(*p5[5])(int, int) = {
    
    0, ADD,SUB,MUL,DIV };//数组下标从零开始,我们可以把数组下标为0的元素直接初始化为0
	test();
	do
	{
    
    
		int x, y;
		printf("请输入要进行的操作:");
		scanf("%d", &num);
		switch (num)
		{
    
    
		case 0:
			printf("程序即将退出\n");
			break;
		case 1:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 2:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 3:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		case 4:
			printf("\n请输入要进行的操作的两个数:");
			scanf("%d %d", &x, &y);
			printf("%d\n", p5[num](x, y));
			break;
		default:
			printf("输入错误,请重新输入!\n");
			break;
		}
	} while (num);
	return 0;
}

insert image description here
We can directly call the corresponding function with the input number, but we found that some codes in this code are redundant, so we can encapsulate the redundant code into a function.

6. A pointer to an 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.
After deriving the pointers above, we can easily write a pointer to an array of function pointers:

int(*(*p6)[5])(int, int);//指向函数指针数组的指针
//p6先和*结合,代表p5是一个指针,剩下的为int(* [ ])(int, int)就是函数指针数组
//p6是一个数组指针,指向的的数组有5个元素。
//每个元素的类型是一个函数指针类型int(*)(int,int)

7. Callback function

A callback function is a function that is called through a function pointer . If a function pointer (address) is passed as a parameter to another function, when this pointer is used to call the function it points to, we say that this is a callback function. The callback function is not directly called by the implementation method 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. Next, we use our own sorting to imitate the qsort function
in the library function .

void Swap(char* p1, char* p2,int width)//交换两个元素的值
{
    
    
	for (int i = 0; i < width; i++)
	{
    
    
		char ch = *p1;
		*p1 = *p2;
		*p2 = ch;
		p1++;
		p2++;
	}
}
int cmp_int(void* p1,void* p2)//整形比较方法
{
    
    
	return *((int*)p1) - *((int*)p2);
}
int cmp_float(void* p1, void* p2)//浮点型比较方法
{
    
    
	if (*(float*)p1 - *(float*)p2 > 0)
	{
    
    
		return 1;
	}
	else if (*(float*)p1 - *(float*)p2 == 0)
	{
    
    
		return 0;
	}
	else
	{
    
    
		return -1;
	}
}
void Bubble_sort(void *arr,int size,int width,int (*p)(const void *p1, const void* p2))//排序函数
{
    
    
	for (int i = 0; i < size - 1; i++)
	{
    
    
		for (int j = 0; j < size - i - 1; j++)
		{
    
    
			if (p((char*)arr + j * width, (char*)arr + (j + 1) * width) > 0)
			//把void*强制转化为char*,通过width来增加地址。
			{
    
    
				Swap((char*)arr + j * width, (char*)arr + (j + 1) * width, width);
				//对要交换元素一个字节一个字节进行交换
			}
		}
	}
}
void test1()
{
    
    
	int arr[10] = {
    
     1,5,9,7,3,8,4,2,6,0 };
	int size = sizeof(arr) / sizeof(arr[0]);
	Bubble_sort(arr,size,sizeof(int), cmp_int);
	//第一个参数:待排序数组的首元素地址
	//第二个参数:待排序的元素个数
	//第三个参数:待排序数组中每个元素的大小
	//第四个参数:函数指针,用来比较两个元素的所用函数的地址-需要我们自己实现。
	//函数指针的两个参数是待比较的两个元素的地址
	for (int i = 0; i < size; i++)
	{
    
    
		printf("%d\t", arr[i]);
	}
	printf("\n");
}
void test2()
{
    
    
	float farr[10] = {
    
     1.1,5.5,9.9,7.7,3.3,8.8,4.4,2.2,6.6,0.5 };
	int size = sizeof(farr) / sizeof(farr[0]);
	Bubble_sort(farr, size, sizeof(float), cmp_float);
	for (int i = 0; i < size; i++)
	{
    
    
		printf("%.1f\t", farr[i]);
	}
}
int main()
{
    
    
	test1();//整形排序
	test2();//浮点形排序
	return 0;
}

insert image description here
We implemented floating point and integer sorting through a Bubble_sort function of our own. Because the Bubble_sort function does not know what type of array is to be sorted, the function we sort uses void * to receive (void * can receive any type of address, but void * cannot be directly dereferenced. So the type must be forced when sorting conversion) . When we use our sorting function to sort, we know the method to compare, so we have to build our own comparison functions cmp_int and cmp_float, if we want to sort character arrays, we can write a character comparison function, through Bubble_sort to make the call . When calling the comparison function, convert void * to char *, and increase the address through width, because the sorting function does not know the data type we want to sort, we can use char to make the byte 1, plus the size of the character we want to compare , to determine the position of each element.
It can be seen from the above example that we do not directly call the comparison function we wrote, but call the function pointer through the sorting function, which is the so-called callback function.

Summarize

Let's look at an example to have a deeper understanding of the content of the pointer.

void (*test(int, void (*)(int)))(int);
//test是一个函数声明
//test函数的参数有2个,第一个是int,第二个是函数指针,该函数指针指向的函数的参数是int,返回值为void
//test函数的返回值也是一个函数指针,该函数指针指向的函数的参数为int,返回值为void
typedef void(*fun)(int);//对void(*)(int)重命名,fun要跟着*,不可以写在最后。
fun test(int, fun);//简化版本

One soldier of the sun will eventually be exhausted, and the contribution will eventually go to the sea. Pointers require us to learn and think repeatedly.

Guess you like

Origin blog.csdn.net/2301_76986069/article/details/130446613