[C Language] In-depth understanding of pointers (1)

Table of contents

Preface

(1) Memory and address

Starting from real life

address

 Memory

 Memory and address are closely related

(2) Pointer variables

Pointer variables and address taking operators

Pointer variables and dereference operators 

pointer size 

Pointer arithmetic

pointer +- integer

 pointer-pointer

 Pointer relational operations

The meaning of pointer type

 void* pointer

 const modified pointer

wild pointer

Causes of wild pointers

 How to avoid wild pointers


 

Preface

         C language is a programming language that directly operates memory. We can directly access and operate the address space in the computer memory.

 

        The pointer type that exists in the C language, the pointer points to the address in the memory. We can access and modify data stored in memory through pointers.

 

         Therefore, having a deep understanding of pointers and understanding memory is very helpful for writing high-quality programs and debugging program failures.


 Before understanding pointers, we first introduce memory and addressConcept:

(1) Memory and address

Starting from real life

address

        Starting from the problem in real life - if you want to go to class, but there are so many teaching buildings in the school, if you want to go to class, you have to search from classroom to classroom. In this way, when you find the classroom where the class is, the get out of class is probably over.

        In real life, how do we solve the problem of low efficiency in finding things?

        ——We can put things in order, become familiar with their locations in advance, and even number them.

        We number each room in each teaching building one by one, so that if you want to go to class, as long as you have the building number and room number, you can quickly find the room and classroom.

Therefore, C language draws on examples in life and thus has the concept of address.​ 

 Memory

        We know that in computing, when the CPU (Central Processing Unit) processes data, the required data is read from the memory, and the processed data will also be put back into the memory.

        When we buy a computer, the memory on the computer is 8GB/16GB/32GB, etc. So how to manage this memory space efficiently?

        In fact, the memory is divided into memory units, and the size of each memory unit is 1 byte.

         How big is a byte?

Common memory units

1byte = 8bit

1KB = 1024byte

1MB = 1024KB

1GB = 1024MB

1TB = 1024GB

1PB = 1024TB

 (It is more appropriate to set the size of a memory unit to 1 byte. If it is too large, the computer's memory will be much smaller. If it is too large, the memory cannot be used efficiently)

 Memory and address are closely related

        Each memory unit also has a number (this number is equivalent to the house number of the teaching building). With the number of this memory unit, the CPU can quickly find a memory space.

       In life, we call house numbers also called addresses. In computers, we call the number of a memory unit also called an address.

        ​​​​​A new name is given to addresses in C language——pointers

So we can understand it as:

        Memory unit number == address == pointer


(2) Pointer variables

Pointer variables and address taking operators

        We have a preliminary understanding of memory and addresses. Let's go back to C language. Creating variables in C language is actually applying for space in memory:

#include <stdio.h>
int main()
{
 int a = 10;
 return 0;
}

 

For example, the above code creates an integer variable a and allocates 4 bytes in the memory to store the integer 10. Each byte has an address. The addresses of the 4 bytes in the picture above are:

0x007DF784
0x007DF785
0x007DF786
0x007DF787

        In the picture above, a strange operator appears - & (address operator)

        Function: Get the address of the variable

 For example, we can put the address of a in pa and print it out with %p:

However, we found that there was only one address printed out, and it was different from the address of a that we saw for the first time!

the reason is:

        1. Every time the code is run, the compiler will reallocate memory space to the variable, which explains why the address printed the second time is different from the first time.

        ​ ​ 2. At the same time, although the integer variable occupies 4 bytes, as long as we know the first byte address, it is feasible to follow the clues to access the 4-byte data.

         In the picture above, another unfamiliar operator appears

Pointer variables and dereference operators 

        The address we get through the address operator (&) is a numerical value, such as: 0x006FFD70. Sometimes this value also needs to be stored for later use. So where do we store such an address value? ?

        The answer is: in the pointer variable.

        We can store the address of the variable by creating a pointer variable:

#include<stdio.h>
int main()
{
    int a = 100;
    int* pa = &a;
    *pa = 0;
}

        *pa means to find the space pointed to by the address stored in pa. *pa is actually a.

        Therefore, the function of the dereference operator is to find the corresponding variable through the address.


pointer size 

        We know that a 32-bit machine assumes 32 address buses. The electrical signal from each address line is converted into a digital signal and is 1 or 0. Then we regard the binary sequence generated by the 32 address lines as an address. Then an address is 32 bits and requires 4 bytes to store.

        If it is a 32-bit machine, the size of the pointer is 4 bytes.

        If it is a 64-bit machine, the size of the pointer is 8 bytes.

int main()
{
 printf("%zd\n", sizeof(char *));
 printf("%zd\n", sizeof(short *));
 printf("%zd\n", sizeof(int *));
 printf("%zd\n", sizeof(double *));
 return 0;
}

 As shown in the figure, demonstration:

         Note that the size and type of pointer variables have nothing to do with each other. As long as variables of pointer type have the same size on the same platform.


Since the size of a pointer has nothing to do with the type, what is the significance of the existence of pointer types?

Before discussing this issue, we first introduce pointer arithmetic

Pointer arithmetic

 There are three basic operations on pointers, namely:

• pointer +- integer

• pointer-pointer

• Relational operations on pointers

pointer +- integer

#include <stdio.h>
//指针+- 整数
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);
 for(i=0; i<sz; i++)
 {
 printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
 }
 return 0;
}

 1. Arrays are stored continuously in memory. As long as you know the address of the first element, you can find all subsequent elements by following the clues.

2. Pointer addition and subtraction of integers means skipping the number of elements of the current pointer type.

 pointer-pointer

//指针-指针
#include <stdio.h>
int my_strlen(char *s)
{
 char *p = s;
 while(*p != '\0' )
 p++;
 return p-s;
}
int main()
{
 printf("%d\n", my_strlen("abc"));
 return 0;
}

 1. What is implemented in the figure is the simulation implementation of strlen function

2. Pointer-pointer represents the number of elements between two pointer variables

 Pointer relational operations

//指针的关系运算
#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,10};
 int *p = &arr[0];
 int i = 0;
 int sz = sizeof(arr)/sizeof(arr[0]);
 while(p<arr+sz) //指针的⼤⼩⽐较
 {
 printf("%d ", *p);
 p++;
 }
 return 0;
}

1. Comparison of pointer relationships is actually a comparison of address sizes.


The meaning of pointer type

         ​​​​ 1. The type of the pointer determines how much authority you have when dereferencing the pointer (how many bytes can be operated at a time).

//代码2
#include <stdio.h>
int main()
{
	int n = 0x11223344;
	char* pc = (char*)&n;
	*pc = 0;
	return 0;

 For the address of n:

 *pc = 0; before

 after:

         ​ ​ ​ 2. The type of pointer determines how far (distance) the pointer can move forward or backward.

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

 

We can see that the pointer variable +1 of type char* skips 1 byte, and the pointer variable +1 of type int* skips 4 bytes. This is the change brought about by the type difference of pointer variables.​ 

 void* pointer

        There is a special type among pointer types, which is the void* type, which can be understood as a pointer without a specific type (or a generic pointer). This type of pointer can be used to accept any type of address. But there are also limitations. Pointers of void* type cannot directly perform pointer +- integer and dereference operations.

 const modified pointer

See detailed explanation

const modified pointericon-default.png?t=N7T8https://mp.csdn.net/mp_blog/creation/editor/134341320

wild pointer

        Concept: A wild pointer means that the position pointed by the pointer is unknowable (random, incorrect, and without clear restrictions)

Causes of wild pointers

1. The pointer is not initialized

2. Pointer out-of-bounds access

3. Release the space pointed to by the pointer

3 examples

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

 How to avoid wild pointers

1. Pointer initialization

2. Be careful when the pointer crosses the boundary

3. When the pointer variable is no longer used, it is set to NULL immediately, and the validity of the pointer is checked before use.

4. Avoid returning the address of local variables

5.assert affirmation


Finished~

Reprinting without the author's consent is prohibited

Guess you like

Origin blog.csdn.net/2301_79465388/article/details/134520467