How to master a sharp weapon of C language - pointer?

List: when learning C language for the first time, everyone must have been tortured by the concept of pointer, pointing here and there for a while, and finally dizzy. This paper starts with some basic concepts and introduces the basic use of pointers.

Memory

Considering that when you first learn C language, you may not know much about the composition principle of computer, so here is a brief introduction to the concept of "memory".

As we all know, everything needs a physical carrier as the basis.

For example, we can't see or touch the "thinking" produced by people, but it doesn't mean that it can exist out of thin air. The physical carrier of thinking is our brain. "Brain" is to "thinking" what "land" is to "human beings".

Similarly, software / code that we can't see or touch also needs a physical carrier similar to "land" and "brain" - memory.

There are two types of memory:

  • Memory: the program running in the computer and the data temporarily generated during operation are here.
  • External memory: those programs that do not need to run temporarily and the final operation results are stored here.

For example, a HelloWorld program:

#include <stdio.h>

int main()
{
    printf("Hello World!\n");
    return 0;
}

After writing and saving, the program will be stored in external memory (hard disk).

When it starts running, the program will be transferred from external memory to run in memory and print HelloWorld.

The above is a simple concept of memory (a very shallow impression): memory can temporarily store data.

What is the structure of memory?

Here we think of memory as a hotel with many rooms, and each room has a unique room number.

People are data; Memory is the hotel.

The duty of the hotel is to provide temporary accommodation; The function of memory is to store data temporarily.

The structure of memory is the same as that of hotels. There are many "rooms" called "memory units", and each memory unit also has a unique "room number" called "memory address". The data "lives" in the memory unit.

Suppose Zhang San lives in room 1001 of the hotel now.

We have the following relationship:

Customer Zhang San lives in room 1001

Put it into memory, that is:

The memory unit with memory address 1001 stores the integer 5

In this way, we can find the corresponding memory unit according to address 1001 and operate the data in it.

But there is a problem, that is, in order to operate 5, we have to remember its address. It is too troublesome for people to remember so many numbers.

Imagine you say hello to others when you say hello to others, good morning, someone's ID number, not "good morning, someone's name".

It's not easy to remember your ID number, let alone others, so our usual name is name. Although the ID number is unique, the name may be repeated.

Yes, it's a name. Use a name instead of an unfriendly memory address. We can give a name to memory unit 1001, just a.

The "name" we take is the "variable name" that programming languages have.

int a = 5;

Variable names are very friendly to us humans. Any zhangsan, lisi, etc. can be used.

Through the variable name, you can access its value. Now we have a variable a, which stores the value 5. We can print its value directly through the variable name:

int a = 5;
printf("%d", a);

But there is also a problem that we don't know the address of a variable.

It's like when you go to the hotel to find Zhang San, you only know his name is Zhang San, but you don't know his room number. What should you do? Knock on the door from room to room?

impossible. We should go to the front desk and ask the staff, "what's Zhang San's room number, please?", The front desk staff will tell us: "1001"

Similarly, to get the address of a variable, we can also ask the "front desk staff": "what is the 'room number' of variable a?", Of course, in the current context, this sentence becomes "what is the memory address of variable a?".

In C language, the role of "front desk staff" is the address operator &.

int a = 5;
printf("%p", &a); //What is the memory address of a?

Through &, we can get the memory address of a variable, usually a string of hexadecimal digits, such as 0061FF1C.

Is everything all right here? no

Pointer

concept

So far, we can only get the memory address of a variable, that is, use &. The question now is how we use it.

Why do people and things in reality have a name? For the convenience of addressing and using.

A name is to a thing what a handle is to a blade. Once a thing has a name, we have the power to use it.

In the program, we will have a large amount of data. In order to use these data, we have the concept of variable and variable name. For example, integer data is stored with integer variables:

int i = 5;
float f = 5.0;
char c = 'x';

Addresses also belong to data. In other words, we should also have some type of variable to store addresses:

int a = 5;
int p = &i; //error code 

Our goal is to use variable p to store the address of variable a of type int, but the above code is wrong. Because our variable p is declared as int type, variable p can only store int type data, not the address of int type variable.

At this time, we need a variable that can store the address of integer variables. C language provides us with a mechanism pointer.

int a = 5;
int *pa = &a;

Now we declare a variable pa that can store the address of int type variables, and then use & to get the address of variable a and assign it to variable pa, which is perfect.

pa here is a pointer. You can see the definition of pointer:

In computer science, a pointer is an object in many programming languages that stores a memory address.

In computer science, an object can be a variable, a data structure, a function, or a method, and as such, is a value in memory referenced by an identifier.

In computer science, pointers are objects that store memory addresses in many languages. The object here can be a variable, structure, function or method.

That is, the memory address is stored in the pointer.

The declaration of pointer needs to use * to indicate that the variable is a pointer variable:

[pointer_type] *[pointer_name];
int a = 5;
float b = 5.0;
char c = 'x';

int *pa = &a; 
float *pb = &b;
char *pc = &c;

Since the pointer stores the address of a variable, we can say that the pointer points to that variable. For example, pa is declared as a pointer to int type, pointing to variable a.

Indirect access operator

We have the address operator & used to get the address of a variable, and we also know how to declare a certain type of pointer to store the address.

If you know how to obtain and store, how to use pointers?

The room number is not used to look good, but to find the room and the people in the room. We have found the room number through & this "front desk staff" and wrote it down. The next step is to come to the door and find the person.

By indirectly accessing the operator *, we can "come to find someone" according to the pointer.

int a = 5; //Store 5 in variable a
int *pa = &a; //Get room number
printf("%d", *pa); //Come and find someone

*pa is the value of the variable pointed to by the pointer pa.

distinguish

It's easy to confuse some concepts when learning C language, so here's a distinction.

int a = 5;
int b = 6;
int c = 7;

int *pa = &a;
int *pb = &b;
int *pc = &b;

printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);

printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
printf("&c = %p\n", &c);

printf("pa = %p\n", pa);
printf("pb = %p\n", pb);
printf("pc = %p\n", pc);

printf("*pa = %d\n", *pa);
printf("*pb = %d\n", *pb);
printf("*pc = %d\n", *pc);

Output as

a = 5
b = 6
c = 7
&a = 0061FF10
&b = 0061FF0C
&c = 0061FF08
pa = 0061FF10
pb = 0061FF0C
pc = 0061FF0C
*pa = 5
*pb = 6
*pc = 6
  • a: Variable
  • &a: A's address
  • int *pa: declare a pointer to type int
  • pa: pointer
  • *PA: variable value pointed to by pointer pa

int *pa is different from * in * pa, which is easy to be confused. When declaring, int * is used together to declare a pointer to an int type variable. Although it is written, it should not be viewed separately.

int a; //Declared a variable a
int *pa; //Declared a variable pa

&And * are a pair of opposite operations, & find the address according to the variable, * find the variable according to the address.

int a = 5;
printf("%d", *&a); //5
printf("%d", a); //5

*&The value of a is 5, that is, a.

initialization

After declaring a variable, we must initialize it before using it.

For example, while declaring variable a, initialize it to 5:

int a = 5;

You can also declare and then initialize:

int a;
a = 5;

If not initialized, the value of the variable will be unimaginable.

Pointers are also variables and must also be initialized. Run the following code first:

int *p;
*p = 5;
return 0;

The meaning of this code is very simple: declare a pointer p and assign 5 to the variable pointed to by pointer p. But this code is wrong!

Who does the pointer p point to? Since we didn't initialize it, we don't know who the pointer p points to. How to assign it?

It's like a person saying to you, "please give this package to Li Si". But you don't know who Li Si is or where Li Si lives. How can you give it?

The courier can deliver the goods without knowing you. That's because there is an address on the package, which is enough.

But in the above code, did you tell the p address? No, Because we didn't initialize the pointer!

So initializing pointer is very important!!! Uninitialized pointer cannot be used!!!

Change as follows:

int a = 4;
int *p = &a;
*p = 5;

Or:

int a = 4;
int *p;
p = &a;
*p = 5;

Now the value of variable a changes from 4 to 5.

Because we wrote the address of variable a on the "package", we can send 5 to variable a.

assignment

We can assign one pointer to another.

int a = 5;

int *p1 = &a;
int *p2;
p2 = p1;

We assign the value of pointer p1 to p2, and then print the following:

printf("a = %d\n", a);
printf("&a = %p\n", &a);

printf("p1 = %p\n", p1);
printf("p2 = %p\n", p2);

printf("*p1 = %d\n", *p1);
printf("*p2 = %d\n", *p2);

printf("&p1 = %p\n", &p1);
printf("&p2 = %p\n", &p2);

Output is:

a = 5
&a = 0061FF1C
p1 = 0061FF1C
p2 = 0061FF1C
*p1 = 5
*p2 = 5
&p1 = 0061FF18
&p2 = 0061FF14

It can be seen that the result of assigning pointer p1 to another pointer p2 is that p2 points to where p1 points. In this way, we can manipulate variable a through two pointers.

*p1 = 4;
printf("a = %d\n", a); //From 5 to 4

*p2 = 3;
printf("a = %d\n", a); //From 4 to 3

Null pointer

The value of a NULL pointer is NULL, indicating that it does not point to any object.

int *p = NULL;

When we initialize a pointer, if we don't know who to point to, we initialize it as a null pointer.

Some usage

We have taken "pointer to variable" as an example to introduce the basic usage of pointer. Now let's introduce some other uses of pointers.

Pointer to pointer

Previously, we introduced "pointer to variable":

int a = 5;
int *pa = &5;

Pointer is also a variable, but it is a little special compared with other types of variables. Pointer variables store the addresses of other variables.

In other words, as a variable, the pointer also has an address, which can be stored by other pointers, that is, the pointer pointing to the pointer.

The corresponding codes are as follows:

int a = 5;
int *pa = &5;
int **ppa = &pa;

As you can see, declaring a "pointer to pointer" requires two *:

[pointer_type] **[pointer_name];

Similarly, to obtain the variable value pointed to by the pointer, you need to make two indirect accesses, namely * * ppa.

Please carefully understand the following codes:

#include <stdio.h>

int main()
{
    int a = 5;
    int *pa = &a;
    int **ppa = &pa;
    
    printf("a = %d\n", a);
    printf("&a = %p\n", &a);

    printf("pa = %p\n", pa);
    printf("*pa = %d\n", *pa);
    printf("&pa = %p\n", &pa);

    printf("ppa = %p\n", ppa);
    printf("*ppa = %p\n", *ppa);
    printf("**ppa = %d\n", **ppa);
    printf("&ppa = %p\n", &ppa);
    
    return 0;
}

Through the code, we can get the following equivalence relationship:

expression Equivalent expression
a 5
pa &a
ppa &pa
*pa a,5
*ppa pa,&a
**ppa *pa,a,5

By analogy, you can also try {pointer to [pointer to (pointer)].

Pointers and arrays

First run the following code:

int arr[5] = {1, 2, 3, 4, 5};
printf("arr = %p\n", arr);

int *p = &arr[0];
printf("&arr[0] = %p\n", &arr[0]);
printf("p = %p\n", p);
printf("arr[0] = %d\n", arr[0]);
printf("*p = %d\n", *p);

p++;
printf("function p++after...\n");

printf("&arr[1] = %p\n", &arr[1]);
printf("p = %p\n", p);
printf("arr[1] = %d\n", arr[1]);
printf("*p = %d\n", *p);

Output is:

arr = 0061FF08
&arr[0] = 0061FF08
p = 0061FF08
arr[0] = 1
*p = 1
 function p++after...
&arr[1] = 0061FF0C
p = 0061FF0C
arr[1] = 2
*p = 2

The following conclusions can be drawn:

  • Arr = & arr [0], arr is the pointer to the first element of the array
  • Int * P = & arr [0] and int *p = arr are equivalent
  • arr[n] and * (p+n) are equivalent

Pointers and functions

Run the following functions first:

#include <stdio.h>

void swap(int x, int y)
{
    int temp = x;
    x = y;
    y = temp;
}

int main()
{
    int x = 5, y = 10;
    printf("Before exchange x = %d, y = %d\n", x, y);
    swap(x, y);
    printf("After exchange x = %d, y = %d\n", x, y);
    return 0;
}

The purpose of the swap function is simple: pass in two values and exchange them.

But the result was disappointing - there was no exchange at all. Why?

We print something:

#include <stdio.h>

void swap(int x, int y)
{
    printf("stay swap()In, x Your address is%p,y Your address is%p\n", &x, &y);
    printf("swap() Before exchange x = %d, y = %d\n", x, y);
    int temp = x;
    x = y;
    y = temp;
    printf("swap() After exchange x = %d, y = %d\n", x, y);
}

int main()
{
    int x = 5, y = 10;
    printf("stay main()In, x Your address is%p,y Your address is%p\n", &x, &y);
    printf("main() Before exchange x = %d, y = %d\n", x, y);
    swap(x, y);
    printf("main() After exchange x = %d, y = %d\n", x, y);
    return 0;
}

Output is:

stay main()In, x Your address is 0061 FF1C,y Your address is 0061 FF18
main() Before exchange x = 5, y = 10
 stay swap()In, x Your address is 0061 FF00,y Your address is 0061 FF04
swap() Before exchange x = 5, y = 10
swap() After exchange x = 10, y = 5
main() After exchange x = 5, y = 10

It can be seen that in swap(), we did exchange values, but when the swap() function returned to main(), the values were not exchanged.

You can see that the addresses of X and Y in swap() and X and Y in main() are not the same, which means * * this x and Y is not that X and y * *.

The reason is very simple. The parameter transfer of swap(int x, int y) is value transfer. The so-called value transfer is to copy the value of the actual parameter to the corresponding memory unit of the formal parameter. The function operates on the memory unit of the formal parameter. No matter how the formal parameter changes, it will not affect the actual parameter.

void swap(int x, int y) //xy is a formal parameter
{.....}

int main()
{
    int x = 5, y = 10;
    swap(x, y); //xy is an argument
}

This explains why the addresses of x and y printed by main() and swap() are different, and why the exchange fails.

Then, in order to operate the arguments directly through the function, we must make the formal parameters and arguments the same memory. So we directly pass the address of the argument to the function, that is, the parameter of the function is a pointer to the memory unit of the argument. This parameter is passed as an address.

Address passing ensures that the change of formal parameter is the change of actual parameter.

Code correction:

#include <stdio.h>

void swap(int *px, int *py) //The formal parameter is a pointer and receives the address of the argument
{
    printf("stay swap()In, px = %p,py = %p\n", px, py);
    printf("swap() Before exchange x = %d, y = %d\n", *px, *py);
    int temp = *px;
    *px = *py;
    *py = temp;
    printf("swap() After exchange x = %d, y = %d\n", *px, *py);
}

int main()
{
    int x = 5, y = 10;
    printf("stay main()In, x Your address is%p,y Your address is%p\n", &x, &y);
    printf("main() Before exchange x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("main() After exchange x = %d, y = %d\n", x, y);
    return 0;
}

Output is:

stay main()In, x Your address is 0061 FF1C,y Your address is 0061 FF18
main() Before exchange x = 5, y = 10
 stay swap()In, px = 0061FF1C,py = 0061FF18
swap() Before exchange x = 5, y = 10
swap() After exchange x = 10, y = 5
main() After exchange x = 10, y = 5

Pointers and structures

Define a structure first:

typedef struct Node {
    int data;
    struct Node *next;
} Node;

Then declare a structure:

Node node;

To access the structure of the body, you need to use it Operator:

node.data;
node.next;

Now we have a pointer to the structure:

Node *p = &node;

To access a member of a structure through a pointer:

(*p).data;
(*p).next;

You can also use the - > operator:

p->data;
p->next;

Note that, -> should be used for pointers to structures.

It's normal for beginners to understand a certain concept. No one can learn to use chopsticks and walk at once. What we need is to spend time on a lot of practice. Take the pointer for example. Beginners find it difficult to understand because they use it less. Conversely, for those who often use C/C + + to write code, the pointer must not be a problem for a long time. So find out the basic principles, and then take time to practice a lot. When the time comes, it will be suddenly enlightened.

Please correct any errors.

If you think it's good, you can pay attention to me.

Posted by UTAlan on Thu, 14 Apr 2022 09:04:17 +0930