[C language] Let you no longer feel headache because of pointers


foreword

Presumably for many students who are new to C language, the knowledge about pointers is a headache. It feels as intricate as a tree branch. In the end, I don’t know where the pointer should point. This article will give a detailed introduction to pointers. I hope they can help you!


1. Pointer

1. What is a pointer?

In computer science, a pointer is an object in a programming language.A pointer is a memory address, and a pointer variable is a variable used to store a memory address, using the memory address can directly point to the memory unit stored in the computer memory, so that the required variable unit can be found, that is, the address points to the variable unit. Therefore, what visualizes the address is called a " pointer ". It means that the memory unit with its address can be found through it.

insert image description here

#include <stdio.h>

int main()
{
    
    
   int a = 10;   //在内存中开辟一块空间
   int *p = &a;  //这里我们对变量a,取出它的地址,可以使用&操作符。
               //将a的地址存放在p变量中,p就是一个指针变量。
   return 0;
}

Summary : A pointer is a variable, a variable used to store an address. (The value stored in the pointer is treated as an address).
Then some people say that pointers are not addresses? How did it become a variable again.
This is equivalent to yourself, your name can represent yourself, others will know you when they mention your name, your name represents you, and the pointer variable represents the address.

On a 32-bit machine, the address is a binary sequence composed of 32 0s or 1s, and the address must be stored in 4 bytes, so the size of the pointer is 4 bytes on the 32-bit platform, and 4 bytes on the 64-bit platform. is 8 bytes.

2. Pointer type

We all know that variables have different types: integer, floating point, etc., and there are corresponding types for pointer variables.

int num = 10;
int* p = &num;

Save &num (the address of num) into p, p is a pointer variable, and its type is int*

char* pc = NULL;  
int* pi = NULL;
short* ps = NULL;
long* pl = NULL;
float* pf = NULL;
double* pd = NULL;

Pointers are defined as: type * . In fact: the char* type pointer is used to store the address of the char type variable, the short* type pointer is used to store the address of the short type variable, and the int* type pointer is used to store the address of the int type variable.

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

insert image description here

  1. &n, pc, pi all represent the starting address of the memory space occupied by the variable n, which is 00C2FB6C
  2. When defining the pc pointer variable, it is forced to be converted to char* type, then when it is +1 operated, the pointer moves backward by one byte, which is 00C2FB6D
  3. Perform +1 operation on the pi pointer variable, and the pointer moves backward four bytes, which is 002CFB70

The meaning of the pointer type :

1. The pointer type determines the size of the space that can be accessed when the pointer is dereferenced. For example, the dereference of the pointer of char* can only access one byte, while the dereference of the pointer of int* can access four words Festival.
2. The pointer type determines the step size of the pointer (how far the pointer can go in one step).

character pointer

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

int main()
{
    
    
    char ch = 'w';
    char *pc = &ch;  //pc就为字符指针
    *pc = 'w';
    return 0;
}
int main()
{
    
    
    char* pstr = "hello world.";//这里是把一个字符串放到pstr指针变量里了吗?
    printf("%s\n", pstr);
    return 0;
}

The code char* pstr = "hello world."; It is easy for students to think that the string hello world. is put into the character pointer pstr, but the essence is to put the address of the first character of the string hello world. into pstr.

2. Pointer arithmetic

1. Pointer ± integer

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

In the arr array, each element type is int type, and the pointer will move backward from the starting position of the array (i * 4) bytes each time arr+i, if the element type is char type, then arr+ The i pointer moves backward i bytes.

insert image description here

2. Pointer-Pointer

int my_strlen(char *s)
{
    
    
	char *p = s;
	while (*p != '\0')
		p++;
	return p - s;
}

int main()
{
    
    
	char str[] = "abcdef";
	int ret = my_strlen(str);
	printf("%d\n", ret);
	return 0;
}

insert image description here

From the output results, we can know that the difference between the pointer and the pointer is the number of elements between the two, and the result has nothing to do with the element type.

3. Relational operations on pointers

for(n = &arr[num-1]; n >= &arr[0];n--)
{
    
    
    *n = 0;
}

From the above code, we can see that the pointer n points to the last element of the arr array, and each cycle n moves forward andCompare with the address of the first element of the array, and finally break out of the loopPointer n points to the address of the memory location before the first element of the arr array. In fact, the task can be successfully completed on most compilers, but we should still avoid writing this way, because the standard does not guarantee that it will work.

standard regulation:

A pointer to an array element is allowed to be compared with a pointer to a memory location after the last element of the array, but not with a pointer to a memory location before the first element.

Three, wild pointer

A wild pointer means that the location pointed to by the pointer is unknown (random, incorrect, and without clear restrictions). If the pointer variable is not initialized when it is defined, its value is random, and the value of the pointer variable is the address of other variables. , which means that the pointer points to a variable whose address is uncertain. To dereference at this time is to access an uncertain address, so the result is unknown.

1. Causes of wild pointers

1.1 The pointer is not initialized

#include <stdio.h>
int main()
{
    
     
    int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
    return 0;
}

1.2 Pointer out-of-bounds access

#include <stdio.h>
int main()
{
    
    
    int arr[10] = {
    
    0};
    int *p = arr;
    int i = 0;
    for(i=0; i<=11; i++)
    {
    
    
        //当指针指向的范围超出数组arr的范围时,p就是野指针
        *(p++) = i;
    }
    return 0;
}

1.3 Release the space pointed to by the pointer

#include <stdio.h>
#include <malloc.h>
int main()
{
    
    
	int *p = (int*)malloc(sizeof(int));
	free(p); //释放掉指针p所指向的内存空间后,就不能在对其解引用访问这块空间,
	         //尽管p的内容依然是那块内存空间
	p = NULL;
	*p = 10;
	printf("%d\n", *p);
	return 0;
}

Let me tell you a little story about this knowledge point: Your girlfriend’s phone number is stored in your mobile phone, and you can call her every day, but one day you broke up and there is no relationship, but your mobile phone is still saved. Your girlfriend's phone number, you said I still want to call her, then your girlfriend can call the police and say that you harassed her, which caused illegal access.

4. Pointer array and array pointer

1. Array of pointers

An array of pointers is an array of pointers.

int* arr[5];

insert image description here

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

2. Array pointer

As I can tell from the name, an array pointer is a pointer that points to an array. (pointer to array)

int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?

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: The priority of [] is higher than that of *, so () must be added to ensure that p is combined with * first.

2.1 & array name VS array name

For an array,The array name represents the address of its first element, so what is the & array name ?

Take a look at this code:

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

insert image description here
You can see that the address printed by the array name and & array name is the same. Are they both the same?
Let's look at this code again

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

insert image description here

According to the above code, we found that although the values ​​of &arr and arr are the same, their meanings should be different.
In fact: &arr represents the address of the entire array, not the address of the first element of the array, it just expresses the same address as the first element.
The address of the first element +1, skipping one element, so the difference between arr+1 and arr is 4, the address of the array +1, skipping the size of the entire array, so the difference between &arr+1 and &arr is 40.

2.2 Use of array pointers

void print_arr(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};
    //数组名arr,表示首元素的地址
    //但是二维数组的首元素是二维数组的第一行
    //所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
    //可以数组指针来接收
    print_arr(arr, 3, 5);
    return 0;
}

Five, array parameters, pointer parameters

1. One-dimensional array parameter passing

#include <stdio.h>

void test(int arr[])// true 
{
    
    }
void test(int arr[10])// true
{
    
    }
void test(int *arr)// true
{
    
    }
void test2(int *arr[20])// true
{
    
    }
void test2(int **arr)//true
{
    
    }

int main()
{
    
    
	int arr[10] = {
    
    0};
	int *arr2[20] = {
    
    0};
	test(arr); //传递一维数组,传递首元素地址,形参可以是数组,也可以是指针
	test2(arr2);// 每个元素都是 int* 类型
}

2. Two-dimensional array parameter passing

void test(int arr[3][5])// true
{
    
    }
void test(int arr[][])// false
{
    
    }
void test(int arr[][5])// true
{
    
    }

//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。

void test(int *arr)// false
{
    
    }
void test(int* arr[5])// false
{
    
    }
void test(int (*arr)[5])// true
{
    
    }
void test(int **arr)// false
{
    
    }

int main()
{
    
    
	int arr[3][5] = {
    
    0};
	test(arr); //首元素为二维数组的第一行的地址,应该由一个数组指针指向
}

3. 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};
	int *p = arr;
	int sz = sizeof(arr)/sizeof(arr[0]);
	//一级指针p,传给函数

	print(p, sz);
	return 0;
}

When the parameter part of a function is a first-level pointer, what parameters can the function receive?

int a = 10;
test(&a);

int* p = &a;
test(p);

int arr[5];
test(arr);

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

When the parameter of the function is a secondary pointer, what parameters can it receive?

char c = 'b';
char* pc = &c;
test(&pc);

char** ppc = &pc;
test(ppc);

char* arr[10];
test(arr);

Six, function pointer

pointer to function

#include <stdio.h>

void add(int x, int y)
{
    
    
	return x + y;
}

int main()
{
    
    
 	printf("%p\n", add);
 	printf("%p\n", &add);

	int (*p)(int x,int y) = &add; // p 为函数指针变量,函数的地址便可存放在 p 指针中
 	return 0;
}

insert image description here

The output is two addresses, which are the addresses of the test function.

//代码1
(*(void (*)())0)();

//代码2
void (*signal(int , void(*)(int)))(int);

Code 1: First, void (*) () is a function pointer internally, and putting it in front of the integer 0 means casting it to a function pointer type. So the overall meaning is to call the function whose first address is 0, and its parameter is no parameter.
Code 2: This is a function declaration, the declared function name is signal, there are two parameters, the first parameter is int, the second parameter is a function pointer, pointing to a function, the function parameter is int, and the return value type is void , and the return type of the signal function is a function pointer, the pointer points to a function, the function parameter is int, and the return value type is void.

//对代码进行简化处理
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

7. Array of function pointers

First of all, the function pointer array is an array, and the elements stored in it are function pointers.

int (*func[10])(int, int) 
//func首先和[]结合,说明是一个数组,数组10个元素,而剩下的表示数组元素的类型(函数指针类型)

Purpose of array of function pointers: transfer table

Next write a simple 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( "请选择:" );
        scanf( "%d", &input);
        switch (input)
        {
    
    
        case 1:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = add(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 2:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = sub(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 3:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = mul(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 4:
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = div(x, y);
              printf( "ret = %d\n", ret);
              break;
        case 0:
                printf("退出程序\n");
				breark;
        default:
              printf( "选择错误\n" );
              break;
       }
	} while (input);  
    return 0;
}

Now there are only four of these operations (addition, subtraction, multiplication and division), but if you want to add some other operations to it, the more complex the function, the more and more case statements below, then we can use the function pointer array to simplify , because the operands and return values ​​of all functions are the same, all function addresses can be stored in the array, and you can select the function at the corresponding coordinates in the array to call through your own choice.

int main()
{
    
    
     int x, y;
     int input = 1;
     int ret = 0;
     int(*p[5])(int x, int y) = {
    
     0, add, sub, mul, div }; //转移表
     while (input)
     {
    
    
          printf( "*************************\n" );
          printf( " 1:add           2:sub \n" );
          printf( " 3:mul           4:div \n" );
          printf( "*************************\n" );
          printf( "请选择:" );
      	  scanf( "%d", &input);
          if ((input <= 4 && input >= 1))
          {
    
    
              printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = (*p[input])(x, y);
          }
          else
          {
    
    
          	  printf( "输入有误\n" );
          }
          printf( "ret = %d\n", ret);
      }
      return 0;
}

8. 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 all function pointers;

//函数指针
int (*pf)(int, int);

//函数指针数组
int (*pfArr[5])(int, int);

//指向函数指针数组的指针
//int(*(*ptr)[5])(int, int) = &pfArr;

Just kidding, you can also store the pointer in an array for infinite dolls.

9. 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.

For the use of the callback function, you can look at the use of the qsort function: link: http://t.csdn.cn/N11rG

That's all for now, thanks for reading!

Guess you like

Origin blog.csdn.net/weixin_47648037/article/details/127378477