Is the pointer really so terrible? Deep anatomy C language high-level pointer, the pointer is here?

Pay attention to one button three times (don't be sure next time)


Hello, bald men. Today, let's talk about the advanced knowledge of the pointer

Key points of this chapter

  1. Character pointer
  2. Array pointer
  3. Pointer array
  4. Array parameter passing and pointer parameter passing
  5. Function pointer
  6. Function pointer array
  7. Pointer to array of function pointers
  8. Callback function
  9. Analysis of pointer and array interview questions

Click - > Initial order of pointer
According to the initial order of the pointer we learned in this section, we should know

  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. Pointers are typed. The pointer type determines the ± integer step size of the pointer and the permission of pointer dereference operation.
  4. Pointer operation.

next! We continue to explore the advanced topic of pointers.

Character pointer

Among the pointer types, we know that one pointer type is character pointer char*

General use:

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'b';
	printf("%c\n", ch);
	return 0;
}

There is another way to use it as follows:

int main()
{
char* pstr = "hello world.";
//Did you put a string into the pstr pointer variable?
printf("%s\n", pstr);
return 0;
}

The essence is to put the address of the first character of the string hello world. Into pstr, rather than the whole string into pstr.


Practice an interview question:

#include <stdio.h>
int main()
{
	char str1[] = "hello world.";
	char str2[] = "hello world.";
	char* str3 = "hello world.";
	char* str4 = "hello world.";
	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;
}

Here, str3 and str4 point to the same constant string. C/C + + stores constant strings in a separate memory area,
When several pointers point to the same string, they actually point to the same block of memory. However, when initializing different arrays with the same constant string, different memory blocks will be opened up.
So str1 and str2 are different, and str3 and str4 are the same.

So we usually write this for constant string pointer variables

const char *str="hello world";

We usually use const to modify non modifiable variables to remind other programmers or let the compiler conduct direct modification check (that is, if I change it, I will report an error directly).

Pointer array

Array can put any data type. Pointer array is essentially an array. The element type stored in the array is pointer, so it is called pointer array, just like integer array and character array

int* arr1[10]; //Array of integer pointers
char *arr2[4]; //Array of first level character pointers
char **arr3[5];//Array of secondary character pointers

Array pointer

Definition of array pointer

Are array pointers pointers? Or an array?
The answer is: pointer.

We are already familiar with:
Integer pointer: int * pint; A pointer that can point to shaped data.
Floating point pointer: float * pf; A pointer that can point to floating point data.
The array pointer should be: a pointer that can point to an array.

int *p1[10];
int (*p2)[10];
//What are P1 and P2? Which is an array pointer?

int (*p)[10]; Explanation: 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.

And int *p[10]; This p is first combined with [10], which is an array. The storage type of the array is int *, so it is called a pointer array.

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

For the following array:

int arr[10];

What are arr and & arr?
We know that arr is the array name, which indicates the address of the first element of the array.
What is the &arr array name?
Let's look at a code:

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

It can be seen that the address printed by the array name and & array name is the same.
Are the two the same?
Let's look at another piece of code:

According to the above code, we find that although the values of & arr and arr are the same, they should have different meanings.
In fact: & arr represents the address of the array, not the address of the first element of the array. (feel it carefully)
The address of the array is + 1, skipping the size of the entire array, so the difference between & arr + 1 and & arr is 40.

Use of array pointers

Since the array pointer points to an array, the address of the array should be stored in the array pointer.

#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
//But we rarely write code like this
return 0;
}

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

After learning pointer array and array pointer, let's review and see the meaning of the following code:

int arr[5];	//integer array 
int *parr1[10];	//Pointer array
int (*parr2)[10];	//Array pointer
int (*parr3[10])[5];	//Array pointer array

Array parameter, pointer parameter

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

One dimensional array parameter transfer

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

It's all right!
An array parameter is passed. The address of the first element of the array is passed (dimensionality reduction occurs)

Two dimensional array parameter transfer

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
//Most importantly, the [] after the first [] is part of the type

void test(int *arr)//ok?//NO
{}
void test(int* arr[5])//ok?//NO
{}
void test(int (*arr)[5])//ok?//YES
{}
void test(int **arr)//ok?//NO
{}
int main()
{
int arr[3][5] = {0};
test(arr);
}
//The first element address of the two-dimensional array is passed, and the first element address is,
//Array address of one-dimensional array & arr,
//Need an array pointer to accept, int(*arr)[5]

Primary pointer transfer parameter

#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};
int *p = arr;
int sz = sizeof(arr)/sizeof(arr[0]);
//First level pointer p, passed to function
print(p, sz);
return 0;
}

Secondary pointer transfer parameter

#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;
}
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? 	// YES, because the address of the first element of the array is passed,
//The first element of the array is a pointer,
//Therefore, the address of the pointer is passed, and a secondary pointer is required to receive it
return 0;
}

Function pointer

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

We can see that the results are the same when printed in the form of p, so separate test and & test must be equal to the function address, with the same value and meaning (different from the array)

How do we save the address of our function?

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();

Obviously pfun1
Pfun1 is first combined with * to explain that pfun1 is a pointer, the pointer points to a function, the pointed function has no parameters, and the return value type is void.

Read two interesting pieces of code:

//Code 1
(*(void (*)())0)();
//Code 2
	void (*signal(int , void(*)(int)))(int);

Code 1:
This is very simple, we can see
Code is a function call

Resolution:

The code converts the 0 forced type to a function address of type void(*) (). Dereferencing the 0 address is to find the function at this address and call this function. The called function is parameterless and the return type is void.

Code 2:
Code is a declaration of a function

Resolution:
The declared function is called signal. This function has two parameters,
They are the function pointer types of int integer and void(*) () respectively. The return value of this function is still the function pointer type of void(*)(int).

Code 2 is too complex. How to simplify it:

typedef void(*)(int) pfun_t;//Wrong writing
//But you can't write that. That's what it means
typedef void(*pfun_t)(int);//Correct writing
pfun_t signal(int, pfun_t);

Function pointer array

Array is a storage space for storing data of the same type. We have learned pointer array, such as:

int *arr[10];
//Each element of the array is an int*

The address of the function should be stored in an array. This array is called the function pointer array. How to define the array of function pointers?

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

Which one is right?
The answer is: parr1
Parr1 is first combined with [] to explain that parr1 is an array. What is the content of the array? Is a function pointer of type int (*) ()

Purpose of function pointer array: transfer table
Example: (calculator)

#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	do
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("Please select:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("Input operand:");
			scanf("%d %d", &x, &y);
			ret = add(x, y);
			printf("ret = %d\n", ret);
			break;
		case 2:
			printf("Input operand:");
			scanf("%d %d", &x, &y);
			ret = sub(x, y);
			printf("ret = %d\n", ret);
			break;
		case 3:
			printf("Input operand:");
			scanf("%d %d", &x, &y);
			ret = mul(x, y);
			printf("ret = %d\n", ret);
			break;
		case 4:
			printf("Input operand:");
			scanf("%d %d", &x, &y);
			ret = div(x, y);
			printf("ret = %d\n", ret);
			break;
		case 0:
			printf("Exit program\n");
			break;
		default:
			printf("Selection error\n");
			break;
		}
	} while (input);
	return 0;
}

Obviously, this most basic method is too redundant
So let's look at the implementation of function pointers

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int add(int a, int b)
{
	return a + b;
}
int sub(int a, int b)
{
	return a - b;
}
int mul(int a, int b)
{
	return a * b;
}
int div(int a, int b)
{
	return a / b;
}
int main()
{
	int x, y;
	int input = 1;
	int ret = 0;
	int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //Transfer table
	while (input)
	{
		printf("*************************\n");
		printf(" 1:add 2:sub \n");
		printf(" 3:mul 4:div \n");
		printf("*************************\n");
		printf("Please select:");
		scanf("%d", &input);
		if ((input <= 4 && input >= 1))
		{
			printf("Input operand:");
			scanf("%d %d", &x, &y);
			ret = (*p[input])(x, y);
		}
		else
			printf("Incorrect input\n");
		printf("ret = %d\n", ret);
	}
	return 0;
}

Obviously much better than just now!

Pointer to array of function pointers

It sounds very tongue twister, but it's actually very simple
First of all, it is a pointer. This pointer points to an array. What is put in this array? It is the function pointer. As we said before, the array can store any element of the same type, including the function pointer here.

How to define?

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

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.

Let's introduce a function qsort
Click to learn qsort function

#include <stdio.h>
//The user of the qosrt function has to implement a comparison function
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
int main()
{
	int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	int i = 0;
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

Use the callback function to simulate the implementation of qsort (bubbling)

#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
	return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
	int i = 0;
	for (i = 0; i < size; i++)
	{
		char tmp = *((char*)p1 + i);
		*((char*)p1 + i) = *((char*)p2 + i);
		*((char*)p2 + i) = tmp;
	}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))
{
	int i = 0;
	int j = 0;
	for (i = 0; i < count - 1; i++)
	{
		for (j = 0; j < count - i - 1; j++)
		{
			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[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
	//char *arr[] = {"aaaa","dddd","cccc","bbbb"};
	int i = 0;
	bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
	for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

Int here_ The CMP function is a callback function

Analysis of pointer and array written test questions

First of all, this is the result of a 32-bit machine. The size of the pointer is 4 bytes. If it is 64 bits, the size of the pointer is 8 bytes

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//One dimensional array
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));//16
	printf("%d\n", sizeof(a + 0));//4
	printf("%d\n", sizeof(*a));//4
	printf("%d\n", sizeof(a + 1));//4
	printf("%d\n", sizeof(a[1]));//4
	printf("%d\n", sizeof(&a));//4
	printf("%d\n", sizeof(*&a));//16
	printf("%d\n", sizeof(&a + 1));//4
	printf("%d\n", sizeof(&a[0]));//4
	printf("%d\n", sizeof(&a[0] + 1));//4
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//Character array
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));//6
	printf("%d\n", sizeof(arr + 0));//4
	printf("%d\n", sizeof(*arr));//1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4
	printf("%d\n", sizeof(&arr + 1));//4
	printf("%d\n", sizeof(&arr[0] + 1));//4
	//The parameter received by strlen is char*str
	printf("%d\n", strlen(arr));//Random value
	printf("%d\n", strlen(arr + 0));//The random value is the same as above
	printf("%d\n", strlen(*arr));//(* arr) is' a ', and the ASCII value of' a 'is 97. Take 97 as the address, but 97 is a wild pointer, which will cause illegal access
	printf("%d\n", strlen(arr[1]));//ditto
	printf("%d\n", strlen(&arr));//The array pointer is passed. Although the types are different, the address value is the same as that of the first first element, so it is also a random value
	printf("%d\n", strlen(&arr + 1));//It is also a random value. The difference between the value and the upper edge is 6 (number of elements)
	printf("%d\n", strlen(&arr[0] + 1));//It's also a random value
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//seven 		 (remember '\ 0')
	printf("%d\n", sizeof(arr + 0));//4
	printf("%d\n", sizeof(*arr));//1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//4
	printf("%d\n", sizeof(&arr + 1));//4
	printf("%d\n", sizeof(&arr[0] + 1));//4
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr + 0));//6
	//printf("%d\n", strlen(*arr));// Illegal access to wild pointer
	//printf("%d\n", strlen(arr[1]));// ditto
	printf("%d\n", strlen(&arr));//6
	printf("%d\n", strlen(&arr + 1));//Random value
	printf("%d\n", strlen(&arr[0] + 1));//5
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));//4
	printf("%d\n", sizeof(p + 1));//4
	printf("%d\n", sizeof(*p));//1
	printf("%d\n", sizeof(p[0]));//1
	printf("%d\n", sizeof(&p));//4
	printf("%d\n", sizeof(&p + 1));//4
	printf("%d\n", sizeof(&p[0] + 1));//4
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p + 1));//5
	//printf("%d\n", strlen(*p));// The old problem is that the ASCII value of 'a' is passed
	//printf("%d\n", strlen(p[0]));// ditto
	printf("%d\n", strlen(&p));//Random value
	printf("%d\n", strlen(&p + 1));//Random value
	//There is no relationship between the above two. It is no longer the relationship between the number of difference elements, because you don't know what characters are in the four bytes of skipped char * and whether there is' \ 0 '
	printf("%d\n", strlen(&p[0] + 1));//5
}
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int main()
{
	//Two dimensional array
	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
	printf("%d\n", sizeof(*(a[0] + 1)));//4
	printf("%d\n", sizeof(a + 1));//4
	printf("%d\n", sizeof(*(a + 1)));//16
	printf("%d\n", sizeof(&a[0] + 1));//4
	printf("%d\n", sizeof(*(&a[0] + 1)));//16
	printf("%d\n", sizeof(*a));//16
	printf("%d\n", sizeof(a[3]));//16
}

Summary: the meaning of array name:

  1. Sizeof (array name), where the array name represents the entire array, and the size of the entire array is calculated.
  2. &Array name, where the array name represents the whole array, and the address of the whole array is taken out.
  3. In addition, all array names represent the address of the first element.

---------------------------------------------------------------------------

That's all for this issue. Thanks for reading. I'll see you next issue
If there is any mistake, welcome to communicate with us
Pay attention to Zhou Wang

Pay attention to the third company

Tags: Python Java C C++ Interview

Posted on Sun, 17 Oct 2021 23:52:48 -0400 by Pascal P.