This chapter focuses on
- Why is there dynamic memory allocation
- Introduction to dynamic memory functions
malloc
free
calloc
realloc
- Common dynamic memory errors
- A couple of classic pen-based tests
- The flexible array
The text start
1. Why does dynamic memory allocation exist
We have mastered the memory opening methods are:
int val = 20;// Open up four bytes in stack space
char arr[10] = {0};// Create a contiguous space of 10 bytes in stack space
Copy the code
But the above way of opening up space has two characteristics:
1. The size of space is fixed.
2. When you declare an array, you must specify the length of the array. The memory it needs will be allocated at compile time.
But the need for space goes beyond that. Sometimes the amount of space we need is not known until the program is running, so the way the array is compiled is not enough. At this time can only try dynamic save open up.
2. The introduction of dynamic memory functions
2.1 malloc and free
The C language provides a dynamic memory opening function:
void* malloc (size_t size);
Size_t is an unsigned integer. Its header file is #include
.
This function requests a contiguous chunk of memory and returns a pointer to that chunk of memory.
Note:
If it succeeds, a pointer to the cleared space is returned.
If it fails, a NULL pointer is returned, so the return value of malloc must be checked.
The return value is of type void*, so the malloc function does not know the type of open space
It’s up to you.
- If the parameter
size
If 0, malloc’s behavior is standard and undefined, depending on the compiler.
In addition, C language provides another function free, specifically used to do dynamic memory release and reclamation, function prototype is as follows:
void free (void* ptr);
PTR a pointer to a previously allocated block of memory with the same header as malloc: #include
.
Note:
If the PTR argument points to a space that is not dynamically opened, the free function behaves undefined.
If the argument PTR is a NULL pointer, the free function does nothing, so it has no effect on the code.
To avoid wasting space or having the contents of the pointer modified later in the code, we can use the free function and assign the pointer to NULL to prevent these problems, for example:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
// Request memory for 10 integers
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)// check whether p is null
{
// The error code corresponding to the error message is stored in errno
printf("%s\n", strerror(errno));
//strerror Prints the error information corresponding to the error code
}
else// Succeeded in creating space
{
// Use space normally
int i = 0;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); }}// When dynamically requested space is no longer used, it should be returned to the operating system
free(p);// Release the dynamic memory pointed to by p
p = NULL;// Prevent the contents pointed to by p from being modified
return 0;
}
Copy the code
The output result when the space is successfully opened:
Output when space creation fails:
2.2 calloc
C also provides a function called calloc, which is also used for dynamic memory allocation. The prototype is as follows:
void* calloc (size_t num, size_t size);
Size_t num is the number of elements, size is the type size of each element, and the header file is #include
, just like malloc and free.
The difference between:
- The function of the calloc function is
num
A size ofsize
The elements open up a space, andInitialize each byte of space to 0.- It differs from malloc only in that calloc initializes every byte in the requested space to all zeros before returning the address.
Such as:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int i = 0;
int* p = (int*)calloc(10, sizeof(int));
// Initialize the contents of memory to 0 before applying for space
if (p == NULL)// check whether p is null
{
// The error code corresponding to the error message is stored in errno
printf("%s\n", strerror(errno));
//strerror Prints the error information corresponding to the error code
}
else// Succeeded in creating space
{
// Use space normally
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); }}// When dynamically requested space is no longer used, it should be returned to the operating system
free(p);// Release the dynamic memory pointed to by p
p = NULL;// Prevent the contents pointed to by p from being modified
return 0;
}
Copy the code
Output result:
So if we need to initialize the contents of the requested memory space, then we can easily use the calloc function to complete the task.
2.3 realloc
The advent of realloc functions makes dynamic memory management more flexible. Sometimes we will find that the space applied in the past is too small, sometimes we will feel that the space applied is too large, in order to reasonably apply for memory, we will make flexible adjustment to the size of memory. The realloc function can be used to adjust the size of the dynamically opened memory. The function prototype is as follows:
void* realloc (void* ptr, size_t size);
PTR indicates the memory address to be adjusted, and size is the new size after the adjustment. If the append succeeds, it will return the starting position of the adjusted memory. If the append fails, it will return a null pointer.
Such as:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int i = 0;
int* p = (int*)malloc(5 * sizeof(int));
// Create 20 bytes of space
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
else
{
for (i = 0; i < 5; i++)
{
*(p + i) = i;
}
// Use malloc to create 20 bytes of space
}
// Suppose here, 20 bytes is not enough for our use
// We want 40 bytes of space
// Realloc can be used here to adjust dynamically allocated memory
int* p2 =(int*) realloc(p, 40);
for (i = 0; i < 10; i++)
{
printf("%d ", *(p2 + i));
}// If five random values are displayed after 0, 1, 2, 3, and 4 are printed, the space is successfully created
return 0;
}
Copy the code
Output result:
But writing code like this can be quite risky, so be careful when using realloc functions:
- If there is enough memory space to append to the space pointed to by P, the append will directly append to the space pointed to by P, and return the address of P.
- If there is not enough memory to add to the space pointed to by P, the realloc function will find a new area of memory, create a new area of memory, copy the data in the original memory, free the old space, and finally return the address of the new space.
- In case the space fails, we use a new pointer variable to accept the return value of the Realloc function.
Correct way to write it:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
int i = 0;
int* p = (int*)malloc(5 * sizeof(int));
// Open up space for five integers
for (i = 0; i < 5; i++)
{
*(p + i) = i;
}
// Assign values to the five integer variables
int* ptr = (int*)realloc(p, 40);
// Not enough space is created. Use realloc to create a new space
// PTR stores the address of the new space created
if(ptr ! = NULL)// Determine whether the opening is successful
{
p = ptr;
// In case the space fails to open (p = null),
// Use a new pointer variable PTR to accept the return value of the realloc function
for (i = 5; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i)); }}else
{
printf("%s\n", strerror(errno));
}
free(p);// Free up space after use
p = NULL;// Reassign the used space to a null pointer
return 0;
}
Copy the code
Output result:
Common dynamic memory errors
3.1 Dereferencing NULL Pointers
Examples of errors:
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;// If p is NULL, there is a problem
free(p);
return 0;
}
Copy the code
NULL Pointers cannot be dereferenced
Warning:
3.2 Cross-border access to dynamic open space
Examples of errors:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i = 0;
int* p = (int*)malloc(10 * sizeof(int));
// Create space for 10 integers
if (NULL == p)
{
exit(EXIT_FAILURE);
}
for (i = 0; i <= 10; i++)
{
*(p + i) = i;// When I is 10, access is out of bounds
}
free(p);
return 0;
}
Copy the code
Error:
When creating space, you need to pay attention to whether there is enough space. If not, you can use the realloc function we talked about earlier to solve the problem.
3.3 Use free to release non-dynamically allocated memory
Examples of errors:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int a = 10;
int* p = &a;
free(p);
// p does not refer to the memory address of the dynamically opened space
return 0;
}
Copy the code
Error still reported:
You don’t always have to use the free function, all right
3.4 Using free to free a portion of dynamically allocated memory
Examples of errors:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(100);
p++;
free(p);
//p no longer points to the starting location of dynamic memory
return 0;
}
Copy the code
Still get an error:
When using the free function, note that the contents in parentheses must be the starting address of the space previously opened
3.5 Releasing the Same Block of Dynamic Memory Multiple times
Examples of errors:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p = (int*)malloc(100);
free(p);
free(p);// Repeat release
return 0;
}
Copy the code
Still error:
To avoid this problem, we can assign the starting address to a NULL pointer every time we use the free function to free the space.
3.6 Dynamic Memory Opening and Forgetting to Release (Memory Leakage)
Examples of errors:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void test()
{
int* p = (int*)malloc(100);
if(NULL ! = p) {return;
}
}
int main()
{
test();
return 0;
}
Copy the code
A memory leak can be negligible, but the accumulation of memory leaks can be serious, no matter how much memory is used up sooner or later. So we must remember: dynamic space must be released, and the correct release.
4. Some classic pen-based tests
4.1 Topic 1:
Can I copy it successfully?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char* p)
{
p = (char*)malloc(100);// Dynamic memory is opened
}
void Test(void)
{
char* str = NULL;
GetMemory(str);/ / value
strcpy(str, "hello world");// Copy the string
printf(str);
}
int main()
{
Test();
return 0;
}
Copy the code
Output result:
Obviously not, so why?
STR is passed to GetMemory as a value, so the parameter p is a temporary copy of STR. The STR is still NULL when the GetMemory function returns, so the copy of strcpy fails. When the GetMemory function returns, the p parameter is destroyed, and the address of the 100 bytes is lost, causing the 100 bytes to leak and cannot be freed.
So if we want to copy it successfully, what should we do? The code is as follows:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* GetMemory(char* p)
{
p = (char*)malloc(100);
return p;// return the address of the dynamically opened space
}
void Test(void)
{
char* str = NULL;
str = GetMemory(str);
strcpy(str, "hello world");
printf(str);
free(str);// Free up space to prevent memory leaks
str = NULL;
}
int main()
{
Test();
return 0;
}
Copy the code
Output result:
Or use a secondary pointer to change the code as follows:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p)// Secondary pointer receive
{
*p = (char*)malloc(100);
//p is a second-level pointer, and *p means to dereference &str
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);// The address of STR is passed
strcpy(str, "hello world");
printf(str);
// The STR in parentheses corresponds to the beginning of the string
free(str);// Free up space to prevent memory leaks
str = NULL;
}
int main()
{
Test();
return 0;
}
Copy the code
Output result:
4.2 Topic 2:
What happens when you run the program?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
int main()
{
Test();
return 0;
}
Copy the code
Output result:
Why is it such a strange result?
Since p in GetMemory creates a temporary local space to hold “Hello World”, STR returns the first character of “Hello World”, but the space created by P is automatically destroyed when the function is finished. When the content of the STR again want to visit the piece of space, the content was missing or be covered, for example, a popular point is equivalent to you to open a brand for 301 room, you call your friends to live, but your friend to come to the room 301 when you return had been made, and the boss has leased 301 to others, That is, the content of the original space is missing or overwritten.
This problem is also called the return stack address problem because the space created on the stack is automatically destroyed and reclaimed when it is out of range. The memory space allocation diagram is as follows:
4.3 Question 3:
What’s wrong with the program?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p, int num)
{
*p = (char*)malloc(num);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
int main()
{
Test();
return 0;
}
Copy the code
It’s very simple. If you don’t have free space, we talked about it in the first problem, so you can jump back and watch it again.
4.4 Topic 4:
Can the program run successfully?
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if(str ! = NULL) { strcpy(str,"world");
printf(str);
}
}
int main()
{
Test();
return 0;
}
Copy the code
The answer is no, because it’s called illegal access. Why is that?
First, we create 100 bytes of space in dynamic memory. Hello \0 is copied into STR, which is fine, but note that the free function returns 100 bytes of used space to the operating system after copying. However, it is no longer maintained by STR. When space is freed, STR attempts to use the space will be treated as illegal access, and the space will not be used by STR, so the final copy of world\0 will fail. So remember to null address (null pointer) after using the free function
5. C/C++ program memory development
As we have said before, memory is always created in the stack, heap and static areas of the three blocks, but this is a rough way to say, next we will have a more in-depth understanding of the rules of memory, first let’s look at a picture:
Combined with our above schematic map of memory area division, we can more clearly understand the rules of memory opening:
- Stack: During the execution of a function, storage units for local variables in the function can be created on the stack. These storage units are automatically released when the function is finished. Stack memory allocation operations are built into the processor’s instruction set, which is highly efficient, but the allocated memory capacity is limited. The stack area mainly stores the local variables, function parameters, return data, return address and so on allocated by the running function.
- Heap: Usually allocated and released by the programmer. If not released by the programmer, the program may be reclaimed by the OS at the end of the program. The allocation is similar to a linked list.
- Data segment (static area) (static area) stores global variables, static data. The program is released by the system after completion.
- Code snippet: Binary code that holds the body of a function (class member functions and global functions).
With this picture, we can better understand the static keyword modifying a local variable:
In fact, ordinary local variables are allocated space in the stack area, which is characterized by variables created there being destroyed out of scope. But static variables are stored in the data segment (the static section), which is characterized by variables created on it that are not destroyed until the end of the program, so the life cycle is longer.
6. Flexible arrays
You may never have heard of flexible arrays, but they exist. In C99, the last element in a structure is allowed to be an array of unknown size, which is called a “flexible array” member.
Such as:
typedef struct st_type1
{
int a;
int b[];// Flexible array member, size unknown
}type_a;
Copy the code
Error: failed to compile
typedef struct st_type2
{
int a;
int b[0];// Flexible array member, size unknown
}type_b;
Copy the code
6.1 Characteristics of flexible array:
- A flexible array member in a structure must be preceded by at least one other member;
- The size returned by sizeof does not include the memory of the flexible array;
- Structures containing flexible array members allocate memory dynamically using the malloc() function, and allocate more memory than the structure size to fit the expected size of the flexible array.
Such as:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
typedef struct S
{
int a;
int b[0];// A flexible array member
}s;
int main()
{
//sizeof(s) evaluates the sizeof an int, that is, the sizeof the member in front of the flexible array
printf("sizeof(s)= %d\n", sizeof(s));
return 0;
}
Copy the code
Output result:
6.2 Use of flexible arrays
When you use flexible arrays, you have to create a separate space for them
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
int arr[0];// Flexible array member, size unknown
};
int main()
{
// If I want to put 10 integers in my flexible array, I should open up memory like this
struct S* pc = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
Struct S* receives the address of the opened space using a pointer of the custom type struct S*
pc->a = 10;// Assign a value to a
// Then assign the flexible array arr
int i = 0;
for (i = 0; i < 10; i++)
{
pc->arr[i] = i;// Assign a value to each member of arR
}
printf("%d\n", pc->a);// Print the value of a
for (i = 0; i < 10; i++)
{
printf("%d ", pc->arr[i]);// Outputs the value of the flexible array arr
}
free(pc);// Free up the space created
pc = NULL;// Prevent unauthorized access
return 0;
}
Copy the code
Output result:
You can also use the Realloc function to create more space if you run out of space:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
struct S
{
int a;
int arr[0];// Flexible array member, size unknown
};
int main()
{
// If I want to put 10 integers in my flexible array, I should open up memory like this
struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));
Struct S* receives the address of the opened space using a pointer of the custom type struct S*
ps->a = 10;// Assign a value to a
// Then assign the flexible array arr
int i = 0;
struct S* ptr = (struct S*)realloc(ps, sizeof(struct S) + 20 * sizeof(int));
// Create extra space as needed
if(ptr ! = NULL) { ps = ptr;// If the address of the new space is not NULL, the address is assigned to ps as the starting address of the new space
}
for (i = 0; i < 10; i++)
{
ps->arr[i] = i;
}
free(ps);// Free up the space created
ps = NULL;// Prevent unauthorized access
return 0;
}
Copy the code
Debugging result:
6.3 Advantages of flexible arrays
Let’s look at the following two ways:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct st_type
{
int i;
int a[0];
}type_a;
int main()
{
/ / code 1
int i = 0;
type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
// Business processing
p->i = 100;
for (i = 0; i < 100; i++)
{
p->a[i] = i;
}
free(p);
return 0;
}
Copy the code
Method 2:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct st_type
{
int i;
int* p_a;
}type_a;
int main()
{
/ / code 2
type_a* p = (type_a*)malloc(sizeof(type_a));
p->i = 100;
p->p_a = (int*)malloc(p->i * sizeof(int));
// Business processing
int i = 0;
for (i = 0; i < 100; i++)
{
p->p_a[i] = i;
}
// Free up space
free(p->p_a);
p->p_a = NULL;
free(p);
p = NULL;
return 0;
}
Copy the code
Code 1 and code 2 above do the same thing, but the implementation of method 1 has two advantages:
The first benefit is easy memory free
If our code is in a function for someone else to use, you do a secondary memory allocation in it and return the entire structure to the user. The user calls free to free the structure, but the user doesn’t know that members of the structure also need free, so you can’t expect the user to find out. So, if we allocate the structure’s memory and its members’ memory all at once, and return the user a pointer to the structure, the user can do a free to free all of the memory.
The second benefit is to increase access speed
Continuous memory is good for improving access speed and reducing memory fragmentation.
That’s the end of this post. If you think it’s helpful, please like it. See you next time!