C/C ++ memory allocation, pointer and array comparison

Source: Internet
Author: User

From: http://qiuyili68.blog.163.com/blog/static/67531803201111195115375? Fromdm & fromsearch & isfromsearchengine = Yes

 

Memory Allocation Method

In C ++, the memory is divided into five areas, which areHeap, stack, free storage, global/static storage, and constant Storage.
STACK: When a function is executed, the storage units of local variables in the function can be created on the stack. When the function execution ends, 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.

Heap is the memory blocks allocated by new. Their release compilers are not controlled and controlled by our applications. Generally, a new compiler corresponds to a Delete. If the programmer does not release the program, the operating system will automatically recycle it after the program is completed.

The free storage zone is the memory blocks allocated by malloc and so on. It is very similar to the heap, but it uses free to end its own life.

In the global/static storage area, global variables and static variables are allocated to the same memory. In the previous C language, global variables were divided into initialized and uninitialized ones, in C ++, there is no such distinction. They share the same memory zone.

Constant storage area, which is a special storage area. It stores constants and cannot be modified.

Clearly differentiate heap and stack

For example:

Void F () {int * P = new int [5];}

This short sentence contains the heap and stack. When we see new, we should first think that we allocated a heap memory. What about the pointer P? It allocates a stack memory, so this sentence means that the stack memory stores a pointer P pointing to a heap memory. The program will first determine the size of memory allocated in the heap, then call operator new to allocate the memory, then return the first address of the memory, and put it into the stack, the assembly code in vc6 is as follows:

00401028 push 14 h

0040102a call operator new (00401060)

0040102f add ESP, 4

00401032 mov dword ptr [ebp-8], eax

00401035 mov eax, dword ptr [ebp-8]

00401038 mov dword ptr [ebp-4], eax

Stack and stack differences

The main differences are as follows:

1. Different management methods;

2. Different space sizes;

3. Whether fragments can be generated is different;

4. Different Growth directions;

5. Different allocation methods;

6. Different Allocation Efficiency;

Management Method: For stacks, it is automatically managed by the compiler without manual control. For heaps, the release work is controlled by programmers and memory leak is easily generated.

Space size: Generally, in a 32-bit system, the heap memory can reach 4 GB. From this perspective, there is almost no limit on the heap memory. But for the stack, there is usually a certain amount of space. For example, under vc6, the default stack space is 1 MB (as if so, I cannot remember ). Of course, we can modify:

Open the project and choose Project> setting> link, select output from category, and set the maximum value and commit of the stack in reserve.

Note: The minimum reserve value is 4 byte. Commit is retained in the page file of the virtual memory. Compared with the general setting, commit makes the stack open up a large value, memory overhead and startup time may be increased.

Fragmentation problem: for the heap, frequent New/delete operations will inevitably lead to memory space disconnections, resulting in a large number of fragments, reducing program efficiency. For the stack, this problem will not exist, because the stack is an advanced and outgoing queue. They are so one-to-one correspondence that it is impossible to have a memory block popped up from the middle of the stack, before the pop-up, the post-stack content on the stack has been popped up. For details, refer to the data structure.

Growth direction: For the stack, the growth direction is upward, that is, the direction to the memory address increase; For the stack, the growth direction is downward, is to increase towards memory address reduction.

Allocation Method: The heap is dynamically allocated without static allocation. There are two stack allocation methods: static allocation and dynamic allocation. Static allocation is completed by the compiler, such as local variable allocation. Dynamic Allocation is implemented by the alloca function, but the stack dynamic allocation is different from the heap dynamic allocation. Its Dynamic Allocation is released by the compiler without manual implementation.

Allocation Efficiency: the stack is the data structure provided by the machine system, and the computer will provide support for the stack at the underlying layer: allocate a dedicated register to store the stack address, the output stack of the Pressure Stack has dedicated Command Execution, which determines the high efficiency of the stack. The heap is provided by the C/C ++ function library, and its mechanism is very complicated. For example, to allocate a piece of memory, library functions search for available space in heap memory based on certain algorithms (for specific algorithms, refer to data structures/operating systems, if there is not enough space (probably because there are too many memory fragments), it is possible to call the system function to increase the memory space of the program data segment, so that there is a chance to allocate enough memory, then return. Obviously, the heap efficiency is much lower than the stack efficiency.

From this point, we can see that compared with the stack, the use of a large number of new/delete operations may easily cause a large amount of memory fragments; because of the absence of dedicated system support, the efficiency is very low; because it may lead to switching between the user State and the core state, the memory application will become more expensive. Therefore, stacks are the most widely used in applications. Even function calls are completed using stacks. The parameters and return addresses in the function call process are as follows, both EBP and local variables are stored in stacks. Therefore, we should try to use stacks instead of heaps.

Although the stack has so many advantages, but because it is not so flexible as the heap, sometimes it is better to allocate a large amount of memory space.

Whether it is a heap or a stack, it is necessary to prevent cross-border phenomena (unless you intentionally cross-border it), because the cross-border result is either a program crash, either it is to destroy the heap and stack structure of the program and generate unexpected results. Even if the above problem does not occur during your program running, you should be careful, maybe it will collapse at some time. At that time, debugging was quite difficult :)

Common memory errors and Countermeasures

Memory Errors are very troublesome. The compiler cannot automatically detect these errors, which can be captured only when the program is running. Most of these errors do not have obvious symptoms, but they are often invisible and increase the difficulty of error correction. Sometimes the user finds you angrily, but the program has not encountered any problems. When you leave, the error occurs again. Common memory errors and their countermeasures are as follows:
* Memory Allocation fails, but it is used

New programmers often make this mistake because they do not realize that memory allocation will fail. A common solution is to check whether the pointer is null before using the memory. If the pointer P is a function parameter, use assert (P! = NULL. If you use malloc or new to apply for memory, you should use if (P = NULL) or if (P! = NULL.

* Although the memory allocation is successful, it is referenced before initialization.

There are two main causes for this mistake: first, there is no idea of initialization; second, the default initial values of the memory are all zero, resulting in incorrect reference values (such as arrays ). There is no uniform standard for the default initial values of the memory. Although sometimes it is zero, we prefer to trust it without any trust. Therefore, no matter which method is used to create an array, do not forget to assign the initial value. Even the zero value cannot be omitted, so do not bother.

* The memory is allocated successfully and initialized, but the operation is beyond the memory boundary.

For example, when an array is used, the subscript "more than 1" or "less than 1" is often performed. Especially in for loop statements, the number of loops is easy to make a mistake, resulting in array operations out of bounds.

* Forgot to release the memory, causing memory leakage

A function containing such errors loses a piece of memory every time it is called. At the beginning, the system had sufficient memory and you could not see the error. Once a program suddenly died, the system prompts: memory is exhausted.

Dynamic Memory application and release must be paired. The usage of malloc and free in the program must be the same, otherwise there must be an error (the same applies to new/delete ).

* If the memory is released, it will continue to be used.

There are three scenarios:

(1) The object calling relationship in the program is too complex, so it is difficult to figure out whether an object has released the memory. At this time, we should re-design the data structure to fundamentally solve the chaos of Object Management.

(2) The Return Statement of the function is incorrect. Be sure not to return the "Pointer" or "Reference" pointing to "stack memory" because the function body is automatically destroyed when it ends.

(3) After the memory is released using free or delete, the pointer is not set to null. As a result, a "wild pointer" is generated ".

[Rule 1] after applying for memory with malloc or new, check whether the pointer value is null immediately. Prevents the use of memory with NULL pointer values.

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

 

 

Comparison between pointers and Arrays

In C ++/C Programs, pointers and arrays can be replaced with each other in many places, which leads to the illusion that the two are equivalent.

An array is either created in a static storage area (such as a global array) or on a stack. The array name corresponds to (rather than pointing to) a piece of memory, and its address and capacity remain unchanged during the lifetime, only the content of the array can be changed.

A pointer can point to any type of memory block at any time, and its feature is "variable". Therefore, we often use pointers to operate dynamic memory. Pointers are far more flexible than arrays, but they are more dangerous.

The following uses a string as an example to compare the features of pointers and arrays.

1. Modified content

In the following example, the size of character array a is 6 Characters and its content is hello. The content of A can be changed, for example, a [0] = 'x '. The pointer P points to the constant string "world" (in the static storage area with the content of World). The content of the constant string cannot be modified. In terms of syntax, the compiler does not think that the statement P [0] = 'X' is inappropriate, but this statement attempts to modify the content of the constant string and causes a running error.

# Include <iostream. h>

Void main ()
{
Char A [] = "hello ";
A [0] = 'X ';
Cout <A <Endl;
Char * P = "world"; // note that P points to a constant string
P [0] = 'X'; // the compiler cannot find this error.
Cout <p <Endl;
}

2. Content replication and Comparison

The array name cannot be directly copied or compared. In the following example, if you want to copy the content of array a to array B, you cannot use statement B = A. Otherwise, a compilation error will occur. Use the standard library function strcpy for replication. Similarly, if the content of B and A is the same, it cannot be determined by if (B = A). The standard library function strcmp should be used for comparison.

Statement P = A does not copy the content of A, but assigns the address of A to P. To copy the content of A, use the library function malloc as P to apply for a memory with a capacity of strlen (A) + 1 characters, and then use strcpy to copy strings. Similarly, the statement if (P = A) compares not the content but the address, and should be compared using the database function strcmp.
// Array...
Char A [] = "hello ";
Char B [10];
Strcpy (B, A); // B = A cannot be used;
If (strcmp (B, A) = 0) // If (B = A) cannot be used)
...
// Pointer...
Int Len = strlen ();
Char * P = (char *) malloc (sizeof (char) * (LEN + 1 ));
Strcpy (P, A); // do not use P =;
If (strcmp (P, A) = 0) // do not use if (P =)
...

3. Computing memory capacity

The sizeof operator can be used to calculate the array capacity (number of bytes ). In the following example (a), the value of sizeof (a) is 12 (do not forget ''). The pointer P points to A, but the value of sizeof (P) is 4. This is because sizeof (p) obtains the number of bytes of a pointer variable, which is equivalent to sizeof (char *) rather than the memory capacity referred to by P. C ++/C language cannot know the memory capacity referred to by the pointer unless you remember it when applying for memory. Note: When an array is passed as a function parameter, the array will automatically degrade to a pointer of the same type. In Example (B), sizeof (a) is always equal to sizeof (char *) regardless of the size of array *).

Example ()
Char A [] = "Hello World ";
Char * P =;
Cout <sizeof (a) <Endl; // 12 bytes
Cout <sizeof (p) <Endl; // 4 bytes
Example (B)
Void func (char a [1, 100])
{
Cout <sizeof (a) <Endl; // 4 bytes instead of 100 bytes
}

4. pointer parameter transfer memory

If the function parameter is a pointer, do not expect this pointer to apply for dynamic memory. In the following example, the getmemory (STR, 200) Statement of the test function does not enable STR to obtain the expected memory. STR is still null. Why?

Void getmemory (char * P, int num)

{

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

}

Void test (void)

{

Char * STR = NULL;

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

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

}

The fault lies in the getmemory function. The compiler always needs to make a temporary copy for each parameter of the function. The copy of the pointer parameter P is _ p, and the compiler makes _ p = P. If the program in the function body modifies the content of _ p, the content of parameter P is modified accordingly. This is why pointers can be used as output parameters. In this example, _ P applied for a new memory, but changed the memory address indicated by _ p, but P was not changed at all. 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 have to use the pointer parameter to request memory, you should use "pointer to Pointer" instead. See the example below:

Void getmemory2 (char ** P, int num)

{

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

}

Void Test2 (void)

{

Char * STR = NULL;

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

Strcpy (STR, "hello ");

Cout <STR <Endl;

Free (STR );

}
Because the concept of "pointer to Pointer" is not easy to understand, we can use function return values to transmit dynamic memory. This method is simpler. See the example below:

Char * getmemory3 (INT num)

{

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

Return P;

}

Void test3 (void)

{

Char * STR = NULL;

STR = getmemory3 (100 );

Strcpy (STR, "hello ");

Cout <STR <Endl;

Free (STR );

}
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 will automatically die at the end of the function, as shown in the example below:

Char * getstring (void)

{

Char P [] = "Hello World ";

Return P; // the compiler will give a warning

}

Void test4 (void)

{

Char * STR = NULL;

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

Cout <STR <Endl;

}

Use the debugger to track test4 step by step. After executing the STR = getstring statement, STR is no longer a null pointer, but the STR content is not "Hello World" but garbage.

What if I rewrite the preceding example to the following example?

Char * getstring2 (void)

{

Char * P = "Hello World ";

Return P;

}

Void test5 (void)

{

Char * STR = NULL;

STR = getstring2 ();

Cout <STR <Endl;

}

Although the function test5 runs without errors, the design concept of the function getstring2 is incorrect. Because "Hello World" in getstring2 is a constant string located in the static storage zone, it remains unchanged during the lifetime of the program. No matter when getstring2 is called, it returns the same read-only memory block.

5. Eliminate "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. There are two main causes of "wild pointer:

(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 * P = NULL;

Char * STR = (char *) malloc (100 );
 

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

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

Class

{

Public:

Void func (void) {cout <"func of Class A" <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 ". But the strange thing is that I did not encounter any errors when running this program, which may be related to the compiler.

 

From: http://qiuyili68.blog.163.com/blog/static/67531803201111195115375? Fromdm & fromsearch & isfromsearchengine = Yes

 

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.