C/C ++ pointer essence)

Source: Internet
Author: User
Tags define null
C/C ++ pointer (1)

[Abstract]

Pointers are one of the most important concepts in C and C ++ programming, and are also the most confusing andProgramOne of the errors. You can use pointer programming to represent various data structures. You can use pointers to share variables or data structures between the main and called functions to facilitate bidirectional data communication; it can also process memory addresses like assembly languages to compile refined and efficient programs. Pointers greatly enrich the functions of C and C ++ languages.

In this article, we will discuss the pointer in two parts. First, I will discuss the content and operation operations of pointers in the basic article. Readers may have a certain understanding of pointer knowledge. Then I will focus on the various applications of pointers in the usage article, uncover the essence of pointers in daily programming, so that readers can truly understand, understand, and use pointers.

Article 1: Basics

1.1Pointer Concept

When talking about pointers, its flexibility and difficult control make many programmers talk about things, but its direct operations on memory, in data

In terms of operation, it has the advantages of high speed and memory saving, and makes many c ++ programmers deeply love each other. So what is a pointer concept?

In fact, pointers are a type of variables, which include the addresses of other variables or functions. Unlike other variables, a common variable contains actual real data, and a pointer is an indicator that tells the program where data can be found in the memory.

Well, here we can define a pointer as follows: A pointer is a variable that contains the addresses of other variables or functions, and the values stored in it are interpreted as memory addresses.

1.2Pointer content

To put it simply, pointers have four aspects: pointer type, pointer type, pointer value, pointer itself

Memory occupied. The following sections describe these content separately.

1.2.1Pointer type

From the perspective of syntax, the pointer type is to remove the remaining part of the pointer name in the pointer declaration statement. This is the type of the pointer. For example:

Int * IP; // the pointer type is int *

Char * IP; // the pointer type is char *

Int ** IP; // the pointer type is int **

INT (* IP) [5]; // the pointer type is int (*) [5]

1.2.2Type pointed to by pointer

When you access the memory area pointed to by the pointer, the type pointed to by the pointer determines what type the compiler will regard the content in the memory area. From the perspective of syntax, the Pointer Points to the type that is the pointer name in the pointer declaration statement and the pointer declaration character on the left of the name * remove the remaining part. For example:

Int * IP; // The type pointed to by the pointer is int.

Char * IP; // The Pointer Points to a char type.

Int ** IP; // The type pointed to by the pointer is int *

INT (* IP) [5]; // The type pointed to by the pointer is int () [5]

1.2.3Pointer value (or memory zone pointed to by the pointer)

The pointer value, or the memory area or address pointed to by the pointer, is the value stored by the pointer itself. This value will be treated as an address by the compiler rather than a general value. In a 32-bit program, the value of all types of pointers is a 32-bit integer, because the 32-bit program's memory address is all 32-bit long. The memory area pointed to by the pointer starts from the memory address represented by the pointer value, and the length is a memory area of sizeof (type pointed to by the pointer. Later, we will say that the value of a pointer is XX, which means that the pointer points to a memory area with xx as the first address. We will say that a pointer points to a memory area, it is equivalent to saying that the pointer value is the first address of the memory area.

The memory zone pointed to by the pointer and the type pointed to by the pointer are two completely different concepts. In the above example, the pointer has already pointed to a type, but since the pointer has not been initialized, the memory zone it points to does not exist, or it is meaningless.

In the future, every time you encounter a pointer, you should ask: What is the type of this pointer? What is the pointer type? Where does this pointer point?

1.2.4Memory occupied by pointers

The memory occupied by the pointer itself is the memory occupied by the pointer itself. You only need to use the sizeof function (pointer

Type. On a 32-bit platform, the pointer occupies 4 bytes.

The memory occupied by pointers can be used to determine whether a pointer expression is left.

C/C ++ pointer essence (2)

1.3Pointer and Memory Management

You can use pointers to write data to any location in the memory. However, once your program has a wild pointer ("wild" pointer), it points to a wrong position, your data is dangerous-the data stored in the heap may be damaged, the data structure used to manage the heap may be damaged, or even the data of the operating system may be modified, sometimes, the above three damages occur at the same time. Therefore, it is very important to rationally and correctly allocate pointer addresses.

1.3.1Memory Allocation Method

There are three memory allocation methods:

(1) distribution from the static storage area. The program has been allocated when it is compiled, and the program exists throughout the entire runtime. For example, global variables and static variables.

(2) create a stack. When a function is executed, the storage units of local variables in the function can be created on the stack. When the function is executed, these storage units are automatically released. Stack memory allocation computation is built into the processor's instruction set, which is highly efficient, but the memory capacity allocated is limited.

(3) allocate from the stack, also known as dynamic memory allocation. When the program runs, it uses malloc or new to apply for any amount of memory. The programmer is responsible for releasing the memory with free or delete. The lifetime of the dynamic memory is determined by us. It is very flexible to use, but there are also the most problems. Below we will focus on dynamic memory allocation.

1.3.2 malloc/freeKey Points of use

Malloc and free are standard library functions in C/C ++. They are used to apply for dynamic memory and release memory.

The following is a prototype of the malloc function:

Void * malloc (size_t size );

Use malloc to apply for an integer-type memory with a length. The program is as follows:

Int * IP = (int *) malloc (sizeof (INT) * length );

We should focus on two elements: "type conversion" and "sizeof ".

The type returned by the malloc function is void *. Therefore, you must explicitly convert the type when calling malloc and convert void * to the required pointer type.

The malloc function does not recognize the type of memory to be applied for. It only cares about the total number of bytes in the memory. For example, the int variable is 2 bytes in a 16-bit system and 4 bytes in a 32-bit system, and the float variable is 4 bytes in a 16-bit system, it is also 4 bytes in 32 bits. You can use sizeof (type) for testing.

Using the sizeof operator in malloc's "()" is a good style, but be careful when we sometimes get dizzy and write IP = malloc (sizeof (IP )) such a program.

The prototype of function free is as follows:

Void free (void * memblock );

Why isn't the free function as complicated as the malloc function? This is because the pointer p type and the memory capacity it refers to are known in advance, and the statement free (p) can correctly release the memory. If P is a null pointer, no matter how many times the free P operation will fail. If P is not a null pointer, the free operation on P will cause a program running error.

1.3.3 new/deleteKey Points of use

For non-Internal data objects, maloc/free alone cannot meet the requirements of dynamic objects. The constructor must be automatically executed when the object is created, and the Destructor must be automatically executed before the object is extinct. Because malloc/free is a library function rather than an operator and is not controlled by the compiler, it is impossible to impose the tasks of executing constructor and destructor on malloc/free.

Therefore, the C ++ language requires a new operator that can complete dynamic memory allocation and initialization, and a delete operator that can clean up and release memory. note that new/delete is not a database function, but a C ++ operator. Let's take a look at the following example to know what is going on.

Class Object

{

Public:

Object (void) {STD: cout <"initialization" <STD: Endl ;}

~ Object (void) {STD: cout <"Destroy" <STD: Endl ;}

Void initialize (void) {STD: cout <"initialization" <STD: Endl ;}

Void destroy (void) {STD: cout <"Destroy" <STD: Endl ;}

}

Void usemallocfree (void)

{

Object * IP = (Object *) malloc (sizeof (object); // apply for dynamic memory

IP-> initialize (); // Initialization

//...

IP-> destroy (); // clear the job

Free (IP); // releases memory

}

Void usenewdelete (void)

{

Object * IP = new object; // apply for dynamic memory and initialize

//...

Delete IP; // clear and release memory

}

How to Use malloc/free and new/Delete to manage the dynamic memory of Objects

The object-like function initialize simulates the constructor function, and the destroy function simulates the destructor function. In usemallocfree, because malloc/free cannot execute constructor and destructor, you must call the member functions initialize and destroy to complete initialization and clearing. The usenewdelete function is much simpler.

Therefore, we should not attempt to use malloc/free to manage the memory of dynamic objects. We should use new/Delete. because the internal data type "object" does not have a process of construction and analysis, malloc/free and new/delete are equivalent to them. New has built-in sizeof, type conversion, and type security check functions. For non-Internal data objects, new completes initialization while creating dynamic objects.

New/delete is commonly used as follows:

Typeof * IP = new typeof [length];

Class/structure * IP = new class structure;

Generally, the delete IP address is released as follows;

Release the array as follows: Delete [] IP address;

1.3.4What if the memory is exhausted?

If a large enough memory block cannot be found when applying for dynamic memory, malloc and new will return a null pointer, declaring that the memory application failed. There are usually three ways to handle the "memory depletion" problem.

(1) judge whether the pointer is null. If yes, use the return statement to terminate the function immediately. For example:

Void func (void)

{

A * A = new;

If (A = NULL)

{

Return;

}

...

}

(2) judge whether the pointer is null. If yes, use exit (1) to terminate the entire program. For example:

Void func (void)

{

A * A = new;

If (A = NULL)

{

STD: cout <"Memory Exhausted" <STD: Endl;

Exit (1 );

}

...

}

 

(3) set exception handling functions for new and malloc. For example, in Visual C ++, you can use the _ set_new_hander function to set your own exception handling function for new, or enable malloc to use the same exception handling function as new. For more information, see the C ++ user manual.

There is a very important phenomenon to tell you. For 32-bit applications, no matter how malloc and new are used, it is almost impossible to cause "memory depletion ". Because the 32-bit operating system supports "virtual storage" and the memory is used up, the hard disk space is automatically replaced. I don't want to mislead readers. I must emphasize that without error handling, the quality of the program will be poor, and never be compromised.

1.3. 5Eliminate"Wild pointer"

The "wild pointer" is not a null pointer, but a pointer to the "junk" memory. Generally, null pointers are not incorrectly used, because if statements are easy to judge. However, the "wild Pointer" is very dangerous, and the IF statement does not work for it. The "wild pointer" is mainly caused by the following reasons:

(1) pointer variables are not initialized. When a pointer variable is created, it does not automatically become a null pointer. Its default value is random, which means it is random. Therefore, the pointer variable should be initialized at the same time when it is created, either set the pointer to null or set it to direct to the legal memory. For example

Char * IP = NULL;

Char * IP = new char;

(2) After the pointer IP address is free or deleted, It is not set to null, which makes people mistakenly think that the IP address is a valid pointer.

(3) pointer operations go beyond the scope of variables. This situation is hard to prevent. The example program is as follows:

Class

{

Public:

Void func (void) {STD: cout <"func of Class A" <STD: Endl ;}

};

Void test (void)

{

A * P;

{

A;

P = & A; // note the life cycle of

}

P-> func (); // P is a "wild pointer"

}

When the function test executes the statement p-> func (), object A has disappeared, and P points to a, so P becomes a "wild pointer ". However, it is strange that some compilers run this program without errors, which may be related to the compiler.

1.3.6How does the pointer Parameter Pass the memory?

If the function parameter is a pointer, do not expect this pointer to apply for dynamic memory. See the following example:

Void getmemory (char * IP, int num)

{

IP = (char *) malloc (sizeof (char) * num );

}

Void test (void)

{

Char * STR = NULL;

Getmemory (STR, 100); // STR is still null

Strcpy (STR, "hello"); // running error

}

Try to apply for dynamic memory with pointer Parameters

The fault lies in the getmemory function. The compiler always needs to make a temporary copy for each parameter of the function. The IP copy of the pointer parameter is _ IP, and the compiler makes _ IP = IP. if the program in the function body modifies the content of the _ IP address, the content of the parameter IP address will be modified accordingly. This is why pointers can be used as output parameters. In this example, _ IP applies for a new memory, but changes the memory address indicated by _ IP, but the IP address remains unchanged. Therefore, the getmemory function cannot output anything. In fact, each execution of getmemory will leak a piece of memory, because the memory is not released with free.

If you need to use the pointer parameter to request memory, you should use "pointer to pointer (second-level pointer)", as shown in the following example:

Void getmemory (char ** P, int num)

{

* IP = (char *) malloc (sizeof (char) * num );

}

Void test (void)

{

Char * STR = NULL;

Getmemory (& STR, 100); // note that the parameter is & STR, not Str

Strcpy (STR, "hello ");

STD: cout <STR <STD: Endl;

Free (STR );

}

Apply for dynamic memory with pointer to pointer

Of course, we can also use function return values to transmit dynamic memory. This method is simpler, as shown in the following example:

Char * getmemory (INT num)

{

Char * IP = (char *) malloc (sizeof (char) * num );

Return IP;

}

Void test (void)

{

Char * STR = NULL;

STR = getmemory (100 );

Strcpy (STR, "hello ");

STD: cout <STR <STD: Endl;

Free (STR );

}

Use function return values to pass dynamic memory

Although it is easy to use the function return value to pass dynamic memory, some people often use the return statement wrong. It is emphasized that the return statement should not be used to return the pointer pointing to the "stack memory", because the function automatically disappears when the function ends, as shown in the following example:

Char * getstring (void)

{

Char P [] = "Hello World ";

Return P; // the compiler will give a warning

}

Void test (void)

{

Char * STR = NULL;

STR = getstring (); // STR content is junk

STD: cout <STR <STD: Endl;

}

ReturnStatement Return Point"Stack memory"Pointer

Finally, based on the above descriptions, We will summarize the following rules for your reference:

[Rule 1] after applying for memory with malloc or new, you should immediately check whether the pointer value is null to prevent the use of memory with the pointer value being null.

Rule 2: Do not forget to assign initial values to arrays and dynamic memory. Avoid using uninitialized memory as the right value.

Rule 3: avoid overrunning the subscript of an array or pointer. Be careful when "more than 1" or "less than 1" is performed.

[Rule 4] dynamic memory application and release must be paired to prevent memory leakage.

[Rule 5] after the memory is released with free or delete, the pointer is immediately set to null to avoid "wild pointer ".

 

C/C ++ pointer essence (3)

1.4Pointer operation

1.4.1 value assignment

The assignment of pointer variables can be performed in the following forms:

1.4.1.1 pointer variable initialization value assignment is as follows:

Int;

Int * IP = &;

1.4.1.2Assign the address of a variable to a pointer variable of the same data type. For example:

Int;

Int * IP address;

IP = & A; // assign the address of integer variable A to the IP address of the integer pointer variable

1.4.1.3Assign the value of a pointer variable to another pointer variable of the same type. For example:

Int;

Int * pA = &;

Int * pb;

PB = PA; // assign the address of a to the pointer variable Pb

Since both PA and Pb are pointer variables pointing to integer variables, values can be assigned to each other.

1.4.1.4Assign the first address of the array to the pointer variable pointing to the array. For example:

Int A [5], * pA;

Pa = A; // array name indicates the first address of the array, so you can assign the pointer variable Pa to the array

It can also be written as follows:

Pa = & A [0]; // the address of the first element of the array is also the first address of the entire array.

Of course, you can also initialize the value assignment method:

Int A [5], * pA =;

The above are some basic array assignment methods. We will discuss in detail the usage of pointers in arrays later.

1.4.1.5 assign the first address of a string to a pointer variable of the character type. For example:

Char * PC;

PC = "C Language ";

Or use the initialization value assignment method to write as follows:

Char * Pc = "C Language ";

It should be noted that it is not to load the entire string into pointer variables, but to load the first address of the character array storing the string into pointer variables.

1.4.1.6 assign the function entry address to the pointer variable pointing to the function. For example:

INT (* PF )();

PF = f; // F indicates the function name.

1.4.2 addition and subtraction

For pointer variables pointing to an array, you can add or subtract an integer n. if an IP address is a pointer variable pointing to array A, IP + N, IP-N, IP ++, ++ IP, IP --, and -- IP operations are legal. Adding or subtracting an integer n to a pointer variable means moving the pointer to the current position (pointing to an array element) forward or backward n positions. It should be noted that the array pointer variable moves forward or backward a position and the address plus 1 or minus 1 is conceptually different. Because Arrays can have different types, array elements of different types occupy different bytes. For example, if you add 1 to the pointer variable, that is, move one position backward to indicate that the pointer variable points to the first address of the next data element. Instead of adding 1 to the original address, see the following example:

Char A [20];
Int * IP =;
...
IP ++;

In the preceding example, the pointer IP type is int * and it points to int. It is initialized to point to the integer variable. in the following 3rd sentences, the pointer IP is added with 1, and the compiler processes it like this: it adds the pointer IP value with sizeof (INT), in a 32-bit program, is added 4. because the address is in bytes, the address pointed to by the IP address is increased by four bytes from the address of the original variable A to the high address.

Because the length of the char type is one byte, the PTR originally points to the four bytes starting from Unit 0th of array, this point points to the four bytes starting from Unit 4th in array. Let's look at the following example:

Char A [20];
Int * IP =;
...
IP + = 5;

In this example, 5 is added to the IP address, and the compiler processes it like this: add the value of the pointer IP address to the value of 5 sizeof (INT ), in the 32-bit program, 5 is multiplied by 4 = 20. because the address unit is byte, the current IP address points to the address, compared to the address pointed to by the added 5 IP address, moved 20 bytes to the high address. In this example, if the IP address before 5 is not added, it points to the four bytes starting from Unit 0th of array A. After 5 is added, PTR points out of the valid range of array. Although this situation may cause problems in applications, it is possible in terms of syntax. This also reflects the flexibility of pointers.

In the preceding example, if the IP address is subtracted from 5, the process is similar, except that the IP value is subtracted from 5 by sizeof (INT ), the new IP address will move 20 bytes to the lower address direction than the original IP address.

To sum up, after a pointer ipold is added with an integer N, the result is a new pointer ipnew. The ipnew type is the same as the ipold type, ipnew points to the same type as ipold. The value of ipnew will be n byte multiplied by sizeof (type pointed by ipold) than the value of ipold. That is to say, the memory area pointed to by ipnew will move n multiplication sizeof (type pointed by ipold) bytes to the high address direction than the memory area pointed to by ipold.

After a pointer ipold is subtracted from an integer N, the result is a new pointer ipnew. The ipnew type is the same as the ipold type, and ipnew points to the same type as ipold. The value of ipnew will be less than the value of ipold by N sizeof (the type that ipold points to) bytes, that is, the memory area pointed to by ipnew will move n times of sizeof (type pointed to by ipold) bytes to the lower address direction than the memory area pointed to by ipold.

1.4.3Relational operation

Two pointers pointing to different elements in the same array can perform various relational operations. For example:

IP1 = ip2 indicates that IP1 and ip2 point to the same array element.

IP1> ip2 indicates that IP1 is in the high address location.

IP1 <ip2 indicates that ip2 is at a low address <p>

The pointer variable can also be compared with 0. If the IP address is set to a pointer variable, IP = 0 indicates that the IP address is a null pointer and does not point to any variable. IP address! = 0 indicates that the IP address is not a null pointer. Null pointers are obtained by assigning 0 values to pointer variables. For example:

# Define null 0

Int * IP = NULL;

It is different to assign 0 or no value to the pointer variable. When the pointer variable is not assigned a value, it can be any value and cannot be used. Otherwise, unexpected errors may occur. The pointer variable can be used after it is assigned a value of 0, but it does not point to a specific variable.

1.4.4 get address operator '&' and get content operator '*'

The bitwise operator & is a single object operator. Its combination is from right to left. Its function is to take the address of a variable.

The content operator * is a single object operator, and its combination is from right to left, used to indicate the variable referred to by the pointer variable. The variable followed by the * operator must be a pointer variable. Note that the pointer operator * is not the same as the pointer operator * in the pointer variable description. In pointer variable description, '*' indicates the type operator, indicating that the variable after it is the pointer type. The '*' in the expression is an operator used to represent the variable referred to by the pointer variable. Example:

Int A = 12;
Int B;
Int * P;
Int ** PTR;
P = & A; // The result of & A is a pointer of the int type *, pointing to the int type, and pointing to the address
// Address.
* P = 24; // * P result. Here its type is int, and the address it occupies is the address pointed to by P.
PTR = & P; // The result of & P is a pointer. the pointer type is p type plus *. Here it is int **. The
// The Pointer Points to the p type. Here it is int *. The Pointer Points to the address of the pointer.
// P's own address.
* PTR = & B; // * PTR is a pointer, and the result of & B is also a pointer. The types of the two pointers are the same as those of the directed type, therefore, it is no problem to assign a value to * PTR using & B.
** PTR = 34; // * the result of PTR is what PTR points to. Here it is a pointer, and this pointer is done again *
// Operation. The result is an int type variable.

1.4.5 brackets

When interpreting a combination of specifiers, square brackets and parentheses on the right of the identifier take precedence over the "*" sign on the left of the identifier, while square brackets and parentheses combine the same priority from left to right. However, you can use parentheses to change the sequence of conventions.

The rule for reading the comments is "from the inside out ". Starting from the identifier, first check whether there are square brackets or garden brackets on the right side of the identifier. If so, first explain it and then check whether there is a number * on the left. If brackets are closed at any time, you must use the same rules to process the content in the brackets before proceeding.

1.5 pointer expression

If the final result of an expression is a pointer, the expression is called a pointer table. Therefore, pointer expressions also have four elements of a pointer: pointer type, pointer type, pointer memory zone, and memory occupied by the pointer.

 

 

C/C ++ pointer essence (4)

The first thing to note is that pointers in C are a type of data. From this point alone, pointers and INT and float are no big difference, however, the data type such as pointer can be stored very well.

We all know that the program is to be executed in the memory. If it is executed in the memory, there must be a method to determine the location. This method is the memory address. That is to say, all the "components" (commands, data, and so on) in a running program are stored in the memory, that is, they all have addresses. A pointer is a variable used to store these memory addresses. Because the value it stores is the address, this causes the pointer variable to be very different from other types of variables.

Pointers have two major features: one is the value of the pointer variable itself. This value is the address (variable) of other data or the address (function) of the instruction. This address can be understood as a "clue" for indirect access to others, that is, the address stored in the pointer variable can access certain data or redirect commands. Since it is a variable, it must have its own storage space. What is the size of the pointer variable? This is very simple: the pointer is used to store the address, so the length of the pointer variable is of course the length of the machine address bus where it is located! For example, on 32-bit x86, pointers are generally 32-bit, that is, 4 bytes. The second is the pointer variable type. We all know that when we declare a pointer variable, We have to specify the name type, such as char */int *. What does the meaning of the char/INT here mean? I think these types can be understood as "scales" or "Permissions ". As we have just said, the pointer value specifies the accessible location, so the pointer type limits the length of the pointer that can be accessed from this location. For example,
Int * P indicates that when the pointer Variable P accesses the data in the address stored in P using methods such as * P, the system will extract the data of sizeof (INT) length, this data is of course an int value, or the length of sizeof (INT) will be added to the system during operations such as P ++. All in all, the pointer is actually an address plus a length. The address limits the starting position of the pointer, And the length specifies the method by which the system parses the memory data.

With the above instructions, we will be familiar with the definition of forced type conversion of pointers.

It is easy to understand the forced type conversion of pointers, which means that the system uses pointers. For example, from int * To char *, the address in the pointer is not changed, but the type is changed. For example, when P ++ is executed, it was originally increased by 4 bytes (32-bit INT), and after conversion, it became increased by 1 byte (char length ). A little more complicated. This is also the reason for forced type conversion from a regular type to a struct. In Linux network programming, it is often used to forcibly convert a pointer into a struct of a certain protocol header, and then extract the data of this header (due to a recent small study of pcap, familiar with ^_^) or type conversion between pointers of various struct. These conversions are actually different interpretations of the system for a pile of data starting from a certain address. For example, in pcap, the captured Ethernet data packet (of course in memory) address is stored in a u_char pointer, this pointer is actually the starting address of a memory space containing all the information of the entire data packet! However, if you use a char pointer to access the memory segment of a byte, you cannot obtain the correct data. Therefore, you need to convert u_char into the corresponding network protocol struct, then, access them in order. There are many other similar examples, but I think it will be helpful to understand how the pcap library extracts data.

In short, pointers are not mysterious, and are not as difficult as people say. The key is an address. I think the most brilliant and powerful C language, and the most flexible feature is pointer. Pointer, the soul of C is also ~!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.