Memory allocation Management

Source: Internet
Author: User

The great Bill Gates once said:
640K ought to being enough for Everybody-bill Gates 1981

Programmers often write memory-management programs that are often scary. If you do not want to accidents, the only solution is to find all the hidden mines and eliminate them, hiding is not able to hide. The content of this article is much deeper than the general textbook, readers need to read carefully, so as to really understand memory management.

   1, memory allocation method

There are three ways to allocate memory:

(1) Allocation from a static storage area. Memory is allocated at the time of program compilation, and the entire running period of the program is present in this block. For example, global variables, static variables.

(2) Create on the stack. When executing a function, the storage units of local variables within the function can be created on the stack, which are automatically freed when the function is executed at the end. The stack memory allocation operation is built into the processor's instruction set and is highly efficient, but allocates limited memory capacity.

(3) Allocation from the heap, also known as dynamic memory allocation. When the program is running, it uses malloc or new to request any amount of memory, and the programmer is responsible for freeing the memory with free or delete. The lifetime of dynamic memory is determined by us and is very flexible to use, but the problem is the most.

   2. Common memory errors and Countermeasures

A memory error is a very troublesome thing to happen. These errors are not automatically discovered by the compiler and are usually captured when the program is running. And most of these errors are not obvious symptoms, and the hidden, increased the difficulty of error. Sometimes the user angrily to find you, the program did not have any problems, you go, wrong and attack. Common memory errors and their countermeasures are as follows:

* The memory allocation was unsuccessful, but it was used.

Novice programmers often make this mistake because they are unaware that the memory allocation will not succeed. A common workaround is to check if the pointer is null before using memory. If the pointer p is an argument to a function, then at the entrance of the function, assert (P!=null)

Check. If you are using malloc or new to request memory, you should use if (p==null) or if (P!=null) for error-proof handling.

* The memory allocation succeeds, but it is not initialized to reference it.

There are two main causes of this error: one is the idea of no initialization, and the other is to mistakenly assume that the default initial value of the memory is all zero, resulting in a reference to the initial error (for example, an array). There is no uniform standard for what the default initial value of memory is, although sometimes it is a zero value and we would rather believe it to be credible. So no matter how to create an array, do not forget to assign the initial value, even if it is assigned 0 values can not be omitted, do not bother.

* The memory allocation succeeds and has been initialized, but the operation crosses the memory boundary.

For example, the use of arrays often occurs when the subscript "more 1" or "less 1" operation. Especially in a For loop statement, the number of loops can be easily mistaken, resulting in array operations being out of bounds.

* Forgot to release memory, causing memory leaks.

The function that contains this error loses one piece of memory each time it is called. At first, the system has plenty of memory and you can't see the error. One time the program suddenly died, the system appears prompt: memory exhaustion.

Dynamic memory application and release must be paired, the program malloc and free use must be the same number, otherwise there must be errors (new/delete).

* Release the memory and continue to use it.
 
There are three types of cases:

(1) The object call relationship in the program is too complex, it is difficult to know whether an object has freed the memory, at this time should redesign the data structure, fundamentally solve the chaos of object management.

(2) The return statement of the function is incorrectly written, and be careful not to return a pointer or reference to "stack memory" because the memory is automatically destroyed at the end of the function body.

(3) After releasing memory with free or delete, the pointer is not set to null. Causes the "wild pointer" to be produced.

After rule 1 has requested memory with malloc or new, you should immediately check that the pointer value is NULL. Prevents the use of memory with a pointer value of NULL.

Rule 2 Do not forget to assign an initial value to both arrays and dynamic memory. Prevents memory that is not initialized from being used as the right value.

"Rule 3" avoids array or pointer subscript out of bounds, especially beware of "more 1" or "less 1" operations.

"Rule 4" the request and release of dynamic memory must be paired to prevent a memory leak.

"Rule 5" after releasing memory with free or delete, immediately set the pointer to NULL to prevent the "wild pointer" from being produced.

   3. Comparison of pointers and arrays

The pointers and arrays can be substituted for each other in a few places, giving the illusion that the two are equivalent.

Arrays are either created in a static store (such as a global array) or created on a stack. The array name corresponds to (instead of pointing to) a piece of memory whose address and capacity remain constant over the lifetime, and only the contents of the array can be changed.

Pointers can point to any type of block of memory at any time, and its characteristics are "mutable", so we use pointers to manipulate dynamic memory. Pointers are far more flexible than arrays, but they are also more dangerous.

The following example compares the properties of pointers and arrays with strings.

3.1 Modifying content

In example 3-1, the capacity of the character array A is 6 characters, and the content is hello. The contents of a can be changed, such as a[0]= ' X '. The pointer p points to the constant string "world" (in the static store, the content is world), and the contents of the constant string cannot be modified. Syntactically, the compiler does not feel anything wrong with the statement p[0]= ' X ', but the statement attempts to modify the contents of the constant string to cause a run error.

    1. Char a[] = "Hello";
    2. A[0] = ' X ';
    3. cout << a << Endl;
    4. Char *p = "World"; Note P points to the constant string
    5. P[0] = ' X '; The compiler cannot find the error
    6. cout << p << Endl;
Copy Code

Example 3.1 modifying the contents of arrays and pointers

3.2 Content Replication and comparison

You cannot copy and compare directly to a group name. In Example 7-3-2, if you want to copy the contents of array A to array B, you cannot use statement B = A, otherwise a compilation error will occur. The standard library function strcpy should be used for replication. Similarly, comparing the contents of B and A is the same, and cannot be judged by the IF (b==a), should be compared with the standard library function strcmp.

The statement p = A does not copy the contents of a pointer p, but assigns the address of A to P. To copy the contents of a, you can use the library function malloc for p to apply for a piece of memory with a capacity of strlen (a) 1 characters, and then use strcpy for string copying. Similarly, the statement if (p==a) comparison is not the content but the address, should be compared with the library function strcmp.

    1. Array...
    2. Char a[] = "Hello";
    3. Char b[10];
    4. strcpy (b, a); Cannot use B = A;
    5. if (strcmp (b, a) = = 0)//cannot be used if (b = = a)
    6. ...
    7. Pointer...
    8. int len = strlen (a);
    9. Char *p = (char *) malloc (sizeof (char) * (len 1));
    10. strcpy (P,a); Do not use P = A;
    11. if (strcmp (p, a) = = 0)//Do not use if (p = = a)
    12. ...
Copy Code

Example 3.2 copying and comparing the contents of arrays and pointers

3.3 Compute Memory capacity

Use the operator sizeof to calculate the capacity (in bytes) of the array. In Example 7-3-3 (a), the value of sizeof (a) is 12 (be careful not to forget "). Pointer p points to a, but sizeof (p) has a value of 4. This is because sizeof (p) Gets the number of bytes of a pointer variable, which is equivalent to sizeof (char*), not the memory capacity referred to by P. The C/C language has no way of knowing the memory capacity that the pointer refers to, unless you remember it when you request memory.

Note that when an array is passed as an argument to a function, the array is automatically degraded to a pointer of the same type. In Example 7-3-3 (b), sizeof (a) is always equal to sizeof (char *) regardless of the capacity of array A.

    1. Char a[] = "Hello World";
    2. char *p = A;
    3. cout<< sizeof (a) << Endl; 12 bytes
    4. cout<< sizeof (p) << Endl; 4 bytes
Copy Code

Example 3.3 (a) calculating the memory capacity of arrays and pointers

    1. void Func (char a[100])
    2. {
    3. cout<< sizeof (a) << Endl; 4 bytes instead of 100 bytes
    4. }
Copy Code

Example 3.3 (b) array degraded to pointer


4, the pointer parameter is how to pass memory?

If the parameter of the function is a pointer, do not expect to use the pointer to apply dynamic memory. In the example 7-4-1, the statement getmemory (STR, 200) of the test function does not have the desired memory for STR, and STR is still null, why?

    1. void GetMemory (char *p, int num)
    2. {
    3. p = (char *) malloc (sizeof (char) * num);
    4. }
    5. void Test (void)
    6. {
    7. char *str = NULL;
    8. GetMemory (str, 100); STR is still NULL
    9. strcpy (str, "Hello"); Run error
    10. }
Copy Code

Example 4.1 Attempting to request dynamic memory with pointer parameters

The problem is in the function getmemory. The compiler always makes a temporary copy of each parameter of the function, the copy of the pointer parameter P is _p, and the compiler causes _p = p. If the program inside the function modifies the contents of the _p, it causes the contents of the parameter p to be modified accordingly. This is why the pointer can be used as an output parameter. In this case, _p applied for new memory, but changed the memory address referred to by _p, but P did not change. So the function getmemory doesn't output anything. In fact, each time a getmemory is executed, a piece of memory is leaked because the memory is not freed with free.

If you have to use pointer parameters to request memory, you should instead use pointers to pointers, see example 4.2.

    1. void GetMemory2 (char **p, int num)
    2. {
    3. *p = (char *) malloc (sizeof (char) * num);
    4. }
    5. void Test2 (void)
    6. {
    7. char *str = NULL;
    8. GetMemory2 (&STR, 100); Note that the parameter is &STR, not str
    9. strcpy (str, "Hello");
    10. cout<< str << Endl;
    11. Free (str);
    12. }
Copy Code

Example 4.2 requesting dynamic memory with pointers to pointers

Since the concept of pointers to pointers is not easy to understand, we can use function return values to pass dynamic memory. This method is much simpler, see example 4.3.

    1. char *getmemory3 (int num)
    2. {
    3. Char *p = (char *) malloc (sizeof (char) * num);
    4. return p;
    5. }
    6. void Test3 (void)
    7. {
    8. char *str = NULL;
    9. str = GetMemory3 (100);
    10. strcpy (str, "Hello");
    11. cout<< str << Endl;
    12. Free (str);
    13. }
Copy Code

Example 4.3 passing dynamic memory with function return value

Using function return values to pass dynamic memory This method is useful, but often someone uses the return statement incorrectly. It is emphasized that you do not return a pointer to "stack memory" with a return statement, since the memory automatically dies at the end of the function, see Example 4.4.

    1. Char *getstring (void)
    2. {
    3. Char p[] = "Hello World";
    4. return p; The compiler will present a warning
    5. }
    6. void Test4 (void)
    7. {
    8. char *str = NULL;
    9. str = GetString (); The content of STR is garbage
    10. cout<< str << Endl;
    11. }
Copy Code

Example 4.4 Return statement returns a pointer to "stack memory"

Using the debugger to trace Test4, str is no longer a null pointer after executing str = getstring statement, but Str's content is not "Hello World" but garbage.
What happens if example 4.4 is rewritten as example 4.5?

    1. Char *getstring2 (void)
    2. {
    3. char *p = "Hello World";
    4. return p;
    5. }
    6. void Test5 (void)
    7. {
    8. char *str = NULL;
    9. str = GetString2 ();
    10. cout<< str << Endl;
    11. }
Copy Code

Example 4.5 Return statement returns a constant string

The function Test5 runs without errors, but the design concept of the function GetString2 is wrong. Because the "Hello World" in GetString2 is a constant string, it is located in a static store, which is constant throughout the lifetime of the program. Whenever GetString2 is called, it always returns the same "read-only" block of memory.

  5, eliminate "wild hands"

The "Wild pointer" is not a null pointer, it is a pointer to "junk" memory. It is generally not wrong to use a null pointer because it is easy to judge with an if statement. But a "wild pointer" is dangerous, and the IF statement does not work on it. There are two main causes of the "wild pointer":

(1) The pointer variable is not initialized. Any pointer variable that has just been created does not automatically become a null pointer, and its default value is random, and it can be arbitrary. Therefore, the pointer variable should be initialized at the same time it is created, either by setting the pointer to null or by pointing it to legitimate memory. For example

    1. char *p = NULL;
    2. Char *str = (char *) malloc (100);
Copy Code

(2) The pointer p is not set to null after the free or delete, which makes the person mistakenly think P is a valid pointer.

(3) The pointer operation goes beyond the scope of the variable. This is an impossible situation, and the sample program is as follows:

    1. Class A
    2. {
    3. Public
    4. void Func (void) {cout << "Func of Class A" << Endl;}
    5. };
    6. void Test (void)
    7. {
    8. A *p;
    9. {
    10. A;
    11. p = &a; Notice the lifetime of a
    12. }
    13. P->func (); P is the "wild pointer"
    14. }
Copy Code

When the

function test executes the statement P->func (), object A has disappeared, and P is pointing to a, so p becomes the "wild pointer". But the strange thing is that I ran this program without error, which may be related to the compiler.


6, why do you want to new/delete with Malloc/free?

malloc and free are standard library functions of the C/C language, and New/delete is the operator of C. They can all be used to request dynamic memory and free memory.

for objects of non-intrinsic data types, light Maloc/free cannot satisfy the requirements of a dynamic object. Objects are automatically executed when they are created, and the object executes the destructor automatically before it dies. Because Malloc/free is a library function and not an operator, the task of executing constructors and destructors cannot be imposed on malloc/free, not within the control of the compiler.

Therefore, the C language requires an operator new that can perform dynamic memory allocation and initialization, and an operator delete that can perform cleanup and deallocation work. Note New/delete is not a library function. Let's take a look at how Malloc/free and new/delete implement dynamic memory management for objects, as shown in Example 6.

    1. Class OBJ
    2. {
    3. Public:
    4. OBJ (void) {cout << "initialization" << Endl;}
    5. ~obj (void) {cout << "Destroy" << Endl;}
    6. void Initialize (void) {cout << "initialization" << Endl;}
    7. void Destroy (void) {cout << "Destroy" << Endl;}
    8. };
    9. void Usemallocfree (void)
    10. {
    11. obj *a = (obj *) malloc (sizeof (obj)); Request Dynamic Memory
    12. A->initialize (); Initialization
    13. ...
    14. A->destroy (); Clear work
    15. Free (a); Freeing memory
    16. }
    17. void Usenewdelete (void)
    18. {
    19. obj *a = new obj; Request dynamic Memory and initialize
    20. ...
    21. Delete A; Clear and free memory
    22. }
Copy Code

Example 6 How to implement dynamic memory management of objects with Malloc/free and New/delete

The function of the class obj initialize simulates the function of the constructor, and the function destroy simulates the function of the destructor. In function Usemallocfree, because Malloc/free cannot execute constructors and destructors, member functions initialize and destroy must be called to complete initialization and cleanup work. The function usenewdelete is much simpler.

Therefore, we should not attempt to use Malloc/free to complete the memory management of dynamic object, should use New/delete. Because the "objects" of the internal data types do not have a process of construction and destruction, Malloc/free and new/delete are equivalent to them.

Since the function of new/delete completely covered the malloc/free, why did C not put malloc/free out of the elimination? This is because C programs often call C functions, and C programs can only use Malloc/free to manage dynamic memory.

If you release the dynamic object created by new with free, the object can cause a program error because it cannot execute the destructor. If you release "Dynamic Memory for malloc request" with delete, the program is theoretically not error-free, but the program is poorly readable. So new/delete must be paired and malloc/free the same.

  7. How to run out of memory?

If a large enough block of memory is not found when the dynamic memory is requested, malloc and new will return a null pointer declaring the memory request to fail. There are typically three ways to handle the "memory exhaustion" issue.

(1) Determine if the pointer is null, and if so, terminate the function with the return statement immediately. For example:

    1. void Func (void)
    2. {
    3. A *a = new A;
    4. if (a = = NULL)
    5. {
    6. Return
    7. }
    8. ...
    9. }
Copy Code

(2) Determine if the pointer is null, and if so, use Exit (1) to terminate the entire program operation immediately. For example:

    1. void Func (void)
    2. {
    3. A *a = new A;
    4. if (a = = NULL)
    5. {
    6. cout << "Memory exhausted" << Endl;
    7. Exit (1);
    8. }
    9. ...
    10. }
Copy Code

(3) Sets the exception handling function for new and malloc. For example, visual C can use the _set_new_hander function to set a user-defined exception handler for new, or to have malloc enjoy the same exception handler as new. For more information, please refer to the C user manual.

The above (1) (2) usage is most common. If there are multiple applications in a function that require dynamic memory, then the way (1) appears to be inadequate (freeing up memory is cumbersome) and should be handled in a way (2).

Many people do not have the heart to use Exit (1), asked: "Do not write error handlers, let the operating system itself solve the line?" "

does not work. If something like "memory exhaustion" occurs, the application is generally no longer available for rescue. If you do not kill the bad program without exit (1), it may kill the operating system. The truth is like: If you don't shoot the gangsters, the gangsters will commit more sins before they die.

There is a very important phenomenon to tell you. For more than 32-bit applications, no matter how malloc and new are used, it is almost impossible to cause "memory exhaustion." I wrote the test program under Windows 98 with Visual C, see Example 7. This program will run indefinitely and will not terminate at all. Because the 32-bit operating system supports "virtual storage", memory ran out, automatically replaced with hard disk space. I only heard the hard disk crunch, Window 98 is tired of the keyboard, mouse no response.

I can conclude that "memory exhaustion" error handlers are useless for more than 32-bit applications. This can be fun for UNIX and Windows programmers: Anyway the error handler does not work, I will not write, save a lot of trouble.

I do not want to mislead the reader, must emphasize: no error handling will lead to the quality of the program is very poor, do not pound foolish.

    1. void Main (void)
    2. {
    3. float *p = NULL;
    4. while (TRUE)
    5. {
    6. p = new float[1000000];
    7. cout << "Eat memory" << Endl;
    8. if (p==null)
    9. Exit (1);
    10. }
    11. }
Copy Code

Example 7 trying to run out of operating system memory


  8, the use of malloc/free points

The prototype of the function malloc is as follows:

    1. void * malloc (size_t size);
Copy Code

Use malloc to request a block length integer type of memory, the program is as follows:

    1. int *p = (int *) malloc (sizeof (int) * length);
Copy Code

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

* The type of malloc return value is void *, so type conversion is performed explicitly when malloc is called, and void * is converted to the desired pointer type.

* The malloc function itself does not recognize what type of memory to request, it only cares about the total number of bytes in memory. We usually don't remember the exact number of bytes for a variable of type int, float, and so on. For example, the int variable is 2 bytes under the 16-bit system, 4 bytes under 32 bits, and the float variable is 16 bytes under the 4-bit system, and 32 bytes under 4 bits. It is best to use the following procedure for testing:

    1. cout << sizeof (char) << Endl;
    2. cout << sizeof (int) << Endl;
    3. cout << sizeof (unsigned int) << Endl;
    4. cout << sizeof (long) << Endl;
    5. cout << sizeof (unsigned long) << Endl;
    6. cout << sizeof (float) << Endl;
    7. cout << sizeof (double) << Endl;
    8. cout << sizeof (void *) << Endl;
Copy Code

Using the sizeof operator in malloc's "()" is a good style, but beware sometimes we get dizzy and write a program like P = malloc (P).

* The prototype of the function free is as follows:

    1. void free (void * memblock);
Copy Code

Why is the free function not as complex as the malloc function? This is because the type of the pointer p and the amount of memory it refers to is known beforehand, and the statement free (p) frees the memory correctly. If p is a null pointer, then free has no problem with P no matter how many times it is manipulated. If p is not a null pointer, then the free operation of P for two consecutive times causes the program to run incorrectly.

  9, the use of new/delete points

Operator new is much simpler to use than the function malloc, for example:

    1. int *P1 = (int *) malloc (sizeof (int) * length);
    2. int *p2 = new Int[length];
Copy Code

This is because new has built-in sizeof, type conversion, and type safety check functionality. For objects that are not internal data types, new initializes the initialization work while creating the dynamic object. If an object has more than one constructor, the new statement can also have multiple forms. For example

    1. Class OBJ
    2. {
    3. Public:
    4. OBJ (void); constructor with no parameters
    5. OBJ (int x); constructor with one parameter
    6. ...
    7. }
    8. void Test (void)
    9. {
    10. obj *a = new obj;
    11. obj *b = new obj (1); Initial value is 1
    12. ...
    13. Delete A;
    14. Delete B;
    15. }
Copy Code

If you create an array of objects with new, you can only use the parameterless constructor of the object. For example

    1. OBJ *objects = new obj[100]; Create 100 Dynamic objects
Copy Code

Cannot be written

    1. OBJ *objects = new obj[100] (1);//create 100 dynamic objects while assigning the initial value 1
Copy Code

When releasing an array of objects with delete, be careful not to lose the symbol ' [] '. For example

    1. delete []objects; The right usage
    2. Delete objects; Incorrect use of
Copy Code

The latter is equivalent to delete objects[0], missing another 99 objects.

  10, some experience

I know a lot of good-to-do C + + programmers, and very few people can pat their breasts and say they are familiar with pointers and memory management (including myself). I was particularly afraid of pointers when I first learned C, so I didn't use a pointer when I developed my first application (about 10,000 lines C code), and it was too stupid to replace pointers with arrays. Dodge Pointer is not a way, and later I rewrite the software, the amount of code reduced to half the original.

My lessons are:

(1) The more afraid of pointers, the more you use pointers. Not using pointers correctly is definitely not a qualified programmer.

(2) It is necessary to develop the habit of "using the debugger to track the program gradually", only in this way can we find the essence of the problem.

Memory allocation Management

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.