C language memory allocation problems
1. Memory in C is divided into four zones
STACK: used to store the function parameters and local variables in the function. Space allocated by the compiler is automatically released by the compiler after the function is executed.
Heap: used to store space allocated by dynamic allocation functions (such as malloc. It is manually assigned by the programmer and must be released by the programmer using free. If you forget to use free release, the allocated space will remain occupied, leading to memory leakage.
Global: used to store global and static variables. It exists throughout the entire running period of the program and is allocated and released by the compiler.
Text constant area: for example, char * c = "123456"; then "123456" is a text constant, which is stored in the text constant area. It is also controlled by the compiler for allocation and release.
Code area: used to store the binary code of a program.
Example 1)
Int A = 0; // global Zone
Void main ()
{
Int B; // Stack
Char s [] = ABC; // s in the stack, and ABC in the text Constant Area
Char * P1, * P2; // Stack
Char * P3 = 123456; // 123456 in the constant zone, and P3 in the stack
Static int C = 0; // global Zone
P1 = (char *) malloc (10); // P1 is on the stack, and the allocated 10 bytes are on the heap
P2 = (char *) malloc (20); // P2 on the stack, 20 bytes allocated in the heap
Strcpy (P1, 123456); // place 123456 in the constant area
}
Example 2)
// Returns the char pointer.
Char * F ()
{
// The s array is stored on the stack.
Char s [4] = {'1', '2', '3', '0 '};
Return s; // return the address of the S array, but the S array is released after the program runs.
}
Void main ()
{
Char * s;
S = f ();
Printf (% S, S); // print out garbled characters. Because s points to the address, there is no data
}
2. dynamically allocate and release memory
After using malloc to dynamically allocate memory, you must determine whether the allocation is successful and whether the pointer value is null.
After the memory is allocated, initialize the memory unit.
The memory is allocated successfully and does not go out of bounds after initialization.
Use free (p) to release the memory after use. Note that after the memory is released, the value of P will not change. It is still an address value and still points to that memory zone, it's just that the value of this memory zone turns into garbage. To prevent subsequent use of this memory, P = NULL should be immediately after free (P), so that if you want to use it later, it will be determined if p is null.
No.1
Void getmemory (char * P)
{
P = (char *) malloc (100 );
}
Void test (void)
{
Char * STR = NULL;
Getmemory (STR );
Strcpy (STR, hello World );
Printf (STR );
}
What is the result after running the test function?
No. 2
Char * getmemory (void)
{
Char P [] = Hello world;
Retrun P;
}
Void test (void)
{
Char * STR = NULL;
STR = getmemory ();
Printf (STR );
}
Problem 1
No. 3
Void getmemory2 (char ** P, int num)
{
* P = (char *) malloc (Num );
}
Void test (void)
{
Char * STR = NULL;
Getmemory (& STR, 100 );
Strcpy (STR, hello );
Printf (STR );
}
Problem 1
No. 4
Void test (void)
{
Char * STR = (char *) malloc (100 );
Strcpy (STR, hello );
Free (STR );
If (STR! = NULL)
{
Strcpy (STR, world );
Printf (STR );
}
}
Problem 1
My analysis of the above problems:
No.1: The program first applies for a char pointer STR, and points STR to null (that is, the STR contains a null address, * the value of STR is 0 in null ), during the function call process, the following actions are performed: 1. Apply for a char pointer P and 2. Copy the STR content to P (this is what the system did during the parameter transfer ), 3. 100 spaces are applied for the p pointer, and 4 returns the test function. finally, the program copies the string Hello World to the memory space pointed by Str. an error occurred here! The STR space is always null and there is no actual space. A deep understanding of step 1 of function calling makes it difficult to find the problem! (Suggestion: Drawing comprehension)
No. 2: The program first applies for a char pointer STR and points STR to null. when calling a function, the following actions are performed: 1. Apply for an array P [] and assign it to hello World (the size of the array space is 12 ), 2. returns the array name P to the STR pointer (that is, the first address of the array is returned ). so we can print the string "Hello World? Of course not! This is because the last step is missing during function calling. that is, after the return array name in step 2, the function call also needs to be performed to release the memory space. when a function is called, it releases the space occupied by all its variables. so the array space is released, that is to say, the content indicated by STR will not be sure what it is.
No. 3: The correct answer is that hello can be printed, but the memory is leaked!
No. 4: Apply for space, copy strings, and release space. there are no problems with the first three steps. an error occurred when determining the condition in the IF statement, because after a pointer is released, its content is not null, but an uncertain value. therefore, if statements cannot be executed. this is also a famous "wild" pointer problem. therefore, after writing a program to release a pointer, we must make it null. in this way, the "wild" pointer will be avoided. some people say that the "wild" pointer is terrible and may bring unexpected errors.
C language memory alignment
C99 specifies that int, unsigned int, and bool can be used as bit domain types, but almost all compilers have extended these types to allow the existence of other types.
The main purpose of bit domains is to compress storage. The general rules are as follows:
1) if the types of adjacent fields are the same, and the sum of the bit widths is smaller than the sizeof size of the type, the subsequent fields will be stored next to the previous field until they cannot be accommodated;
2) If the Field Types of adjacent bit fields are the same, but the sum of Bit Width is greater than the sizeof size of the type, the subsequent fields start from the new storage unit, its offset is an integer multiple of its type;
3) if the types of adjacent bit field are different, the implementations of each compiler are different. vc6 adopts the non-compression mode and Dev-C ++ adopts the compression mode;
4) do not compress fields that are interspersed with non-bit fields;
5) the total size of the entire struct is an integer multiple of the size of the widest basic type.
Let's take a look at the example.
Example 1:
Struct bf1
{
Char F1: 3;
Char F2: 4;
Char F3: 5;
};
Its memory layout is:
| _ F1 __|__ F2 __| _ | ____ F3 ___ | ____ |
| _ |
0 3 7 8 1316
The bit field type is Char, and the 1st bytes can only accommodate F1 and F2, so F2 is compressed to 1st bytes, while F3 can only start from the next byte. Therefore, the result of sizeof (bf1) is 2.
Example 2:
Struct bf2
{
Char F1: 3;
Short F2: 4;
Char F3: 5;
};
Because the adjacent bit fields have different types, in vc6, sizeof is 6, and in Dev-C ++ is 2.
Example 3:
Struct bf3
{
Char F1: 3;
Char F2;
Char F3: 5;
};
What is memory alignment?
Consider the following structure:
Struct foo
{
Char C1;
Short S;
Char C2;
Int I;
};
Assume that the members of this structure are in a compact arrangement in the memory. If the C1 address is 0, the s address should be 1, and the C2 address is 3, the address of I is 4. That is
C1 00000000, s 00000001, C2 00000003, I 00000004.
However, we write a simple program in Visual C/C ++ 6:
Struct Foo;
Printf ("C1% P, S % P, C2 % P, I % P/N ",
(Unsigned INT) (void *) & A. C1-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. S-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. C2-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. I-(unsigned INT) (void *) & );
Run and output:
C1 00000000, s 00000002, C2 00000004, I 00000008.
Why? This is a problem caused by memory alignment.
Why memory alignment
The following content is excerpted from Intel architecture 32 manual.
Words, dual words, and four words do not need to be aligned in the memory on the natural boundary. (For words, double words, and four words, the natural boundary is an even number of addresses, which can be divided by four, and an address that can be divided by eight .)
In any case, data structures (especially stacks) should be aligned as much as possible on natural boundaries to improve program performance. The reason is that the processor needs to perform two memory accesses to access non-alignment memory; however, alignment memory access only needs one access.
A single-or dual-character operand spans the 4-byte boundary, or a four-character operand spans the 8-byte boundary, which is considered not aligned and thus requires two bus cycles to access the memory. The starting address of a word is odd, but it does not span the word boundary. It is considered to be aligned and can be accessed in a bus cycle.
Some operations require memory operations to be aligned on the natural boundary. If the operands are not aligned, these commands generate a general protection exception (# GP ). The natural boundary of dual words is the address that can be divided by 16 characters. Other double-byte commands allow non-alignment access (without common protection exceptions). However, additional memory bus cycles are required to access non-alignment data in the memory.
Processing of memory alignment by the compiler
By default, the C/C ++ compiler performs memory alignment for the member data in the structure and stack by default. Therefore, the output of the above program becomes:
C1 00000000, s 00000002, C2 00000004, I 00000008.
The compiler shifts unaligned members to the backend, alignment each Member to the natural boundary, which leads to a larger size of the entire structure. Although it sacrifices a little space (there are holes between members), it improves performance.
For this reason, we cannot assert sizeof (FOO) = 8. In this example, sizeof (FOO) = 12.
How to avoid the impact of memory alignment
So can we both improve performance and save some space? TIPS. For example, we can change the above structure:
Struct bar
{
Char C1;
Char C2;
Short S;
Int I;
};
In this way, each member is aligned on its natural boundary, thus avoiding the automatic alignment of the compiler. In this example, sizeof (bar) = 8.
This technique plays an important role, especially when the structure is provided to third-party developers as part of the API. Third-party developers may change the default alignment options of the compiler, causing this structure to use some Alignment Method in your released DLL, while where third-party developers use another Alignment Method. This will cause major problems.
For example, in the foo structure, our DLL uses the default alignment option, alignment is
C1 00000000, s 00000002, C2 00000004, I 00000008, and sizeof (FOO) = 12.
The third party closes the alignment option, resulting in
C1 00000000, s 00000001, C2 00000003, I 00000004, and sizeof (FOO) = 8.
How to Use alignment options in C/C ++
The compilation options in vc6 include/ZP [1 | 2 | 4 | 8 | 16]./zp1 indicates alignment at the boundary of 1 byte, and zpn indicates alignment at the boundary of n Bytes. The N-byte boundary alignment means that the address of a member must be arranged on an integer multiple address of the member size or an integer multiple address of N, and the minimum values of the two must be obtained. That is:
Min (sizeof (Member), n)
In fact, the one-byte boundary alignment means that there is no holes between the structure members.
The/zpn option is applied to the entire project and affects all structures involved in compilation.
To use this option, you can open the project properties page in vc6, C/C ++ page, select the code generation category, and select in struct member alignment.
To use alignment options for some schema definitions, use the # pragma pack compilation command. Command syntax:
# Pragma pack ([show] | [push | Pop] [, identifier], n)
The meaning is the same as that of the/zpn option. For example:
# Pragma pack (1)
Struct foo_pack
{
Char C1;
Short S;
Char C2;
Int I;
};
# Pragma pack ()
Stack memory alignment
We can observe that the stack alignment in vc6 is not affected by the alignment option of the structure member. (There are two things ). It always maintains alignment and alignment on the 4-byte boundary.
Verification Code
# Include <stdio. h>
Struct foo
{
Char C1;
Short S;
Char C2;
Int I;
};
Struct bar
{
Char C1;
Char C2;
Short S;
Int I;
};
# Pragma pack (1)
Struct foo_pack
{
Char C1;
Short S;
Char C2;
Int I;
};
# Pragma pack ()
Int main (INT argc, char * argv [])
{
Char C1;
Short S;
Char C2;
Int I;
Struct Foo;
Struct Bar B;
Struct foo_pack P;
Printf ("Stack C1 % P, S % P, C2 % P, I % P/N ",
(Unsigned INT) (void *) & C1-(unsigned INT) (void *) & I,
(Unsigned INT) (void *) & S-(unsigned INT) (void *) & I,
(Unsigned INT) (void *) & C2-(unsigned INT) (void *) & I,
(Unsigned INT) (void *) & I-(unsigned INT) (void *) & I );
Printf ("struct Foo C1 % P, S % P, C2 % P, I % P/N ",
(Unsigned INT) (void *) & A. C1-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. S-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. C2-(unsigned INT) (void *) &,
(Unsigned INT) (void *) & A. I-(unsigned INT) (void *) & );
Printf ("struct bar C1 % P, C2 % P, S % P, I % P/N ",
(Unsigned INT) (void *) & B. C1-(unsigned INT) (void *) & B,
(Unsigned INT) (void *) & B. C2-(unsigned INT) (void *) & B,
(Unsigned INT) (void *) & B. S-(unsigned INT) (void *) & B,
(Unsigned INT) (void *) & B. I-(unsigned INT) (void *) & B );
Printf ("struct foo_pack C1 % P, S % P, C2 % P, I % P/N ",
(Unsigned INT) (void *) & P. C1-(unsigned INT) (void *) & P,
(Unsigned INT) (void *) & p.s-(unsigned INT) (void *) & P,
(Unsigned INT) (void *) & P. C2-(unsigned INT) (void *) & P,
(Unsigned INT) (void *) & P. I-(unsigned INT) (void *) & P );
Printf ("sizeof foo is % d/N", sizeof (FOO ));
Printf ("sizeof bar is % d/N", sizeof (bar ));
Printf ("sizeof foo_pack is % d/N", sizeof (foo_pack ));
Return 0;
}
From: http://blog.csdn.net/pfgmylove/article/details/3212163