1. Dynamic memory function
Why does dynamic memory allocation exist?
int main(){
int num = 10; //向栈空间申请4个字节
int arr[10]; //向栈空间申请了40个字节
return 0;
}
The above method of opening up space has two characteristics:
- The space allocation size is fixed.
- When the array is declared, the length of the array must be specified, and the memory it needs is allocated at compile time.
But the demand for space is not just the above situation. Sometimes the size of the space we need can only be known when the program is running, and the way to open up space when the array is compiled cannot be satisfied. At this time, dynamic memory development is required.
1.1 malloc and free
malloc()
It is used to dynamically allocate memory during program execution. Its full name is "memory allocation", which means memory allocation. malloc()
Functions are part of the C standard library and their declarations are in stdlib.h
header files.
The function prototype is as follows:
void* malloc(size_t size);
Here, size
is the number of bytes you want to allocate, and the function returns a pointer to the start address of the allocated memory block. malloc()
The return type of the function is void*
, which means that the returned pointer can be assigned to any pointer type without explicit conversion.
Here's a brief explanation malloc()
of how it works:
1. You provide the number of bytes you want to allocate, and
malloc()
search the heap for a contiguous block of memory large enough to store those bytes.2. If it finds a suitable memory block, it marks it as used and returns a pointer to the starting address of the memory block.
3. If it cannot find a large enough memory block, it will return a
NULL
pointer indicating that the memory allocation failed.
Note : Using malloc()
the allocated memory needs to be free()
explicitly released using the function, otherwise it will cause a memory leak.
void free(void* ptr);
free()
The function takes a pointer to a previously allocated block of memory and frees it, making it available for future dynamic allocations. If you forget to free previously allocated memory, your program will consume more memory each time it runs the allocation code and may eventually run out of memory.
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
int* dynamicArray = (int*)malloc(n * sizeof(int));
if (dynamicArray == NULL) {
printf("内存分配失败!\n");
} else {
// 使用分配的内存块
for (int i = 0; i < n; i++) {
dynamicArray[i] = i + 1;
}
// 当不再需要分配的内存时,记得释放它
free(dynamicArray);
dynamicArray = NULL;
}
return 0;
}
It 's good practice, but not required, to set the pointer after calling free()
a function to free dynamically allocated memory .dynamicArray
NULL
Advantages of setting pointers to NULL
:
- Avoid dangling pointers (Dangling Pointer): If the pointer is not set to after freeing the memory
NULL
, the pointer will still retain the previous address. If you continue to use this pointer in subsequent code, it may cause a dangling pointer, that is, the memory pointed to by the pointer has been freed, which may cause the program to crash or produce errors that are difficult to debug. Setting the pointer toNULL
can help you avoid this, because the program will produce an explicit error (null pointer dereference) if you try to use a null pointer.- Avoid double-freeing: After freeing memory, if you set the pointer to
NULL
, you canNULL
determine whether the memory has been freed by checking whether the pointer is . If you mistakenly call it again in subsequent codefree()
, it will result in undefined behavior.
If you are careful to avoid dangling pointers and repeatedly freeing memory in subsequent code, then not setting to NULL
will not cause problems either. However, this is a simple and helpful extra safeguard against bugs, so it is recommended to set the pointer to after freeing the memory NULL
.
1.2 calloc
calloc()
is another dynamic memory allocation function that also belongs to the standard C library (stdlib.h header file). Similar to malloc()
function, but with some differences in usage.
calloc()
The prototype of the function is as follows:
void* calloc (size_t num, size_t size);
where num
is the number of elements you want to allocate and size
is the size in bytes of each element. calloc()
The function allocates space for num * size
a memory block of bytes and initializes all bits in that memory block to zero.
One advantage over malloc()
, calloc()
is that it automatically initializes the allocated memory, which means you don't need to manually zero out the allocated memory. In some cases, this can be very useful, especially when you need to ensure that allocated memory is zero to begin with.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n = 5;
int* dynamicArray = (int*)calloc(n, sizeof(int));
if (dynamicArray == NULL) {
printf("内存分配失败!\n");
} else {
// 使用分配的内存块,这里的内存已经被初始化为零
for (int i = 0; i < n; i++) {
printf("%d ", dynamicArray[i]); // 输出: 0 0 0 0 0
}
// 当不再需要分配的内存时,记得释放它
free(dynamicArray);
}
return 0;
}
Summarize:
calloc = malloc+memset initialized to 0
1.3 realloc
realloc
is a function used to reallocate the size of a memory block. Specifically, it can be used to change the size of a previously passed malloc
or calloc
allocated memory block.
realloc
The declaration of the function is as follows:
void *realloc(void *ptr, size_t size);
Parameter Description:
ptr
: A pointer to a previously allocated memory block. Ifptr
it is NULL,realloc
the behavior is equivalentmalloc
to allocating a new memory block.size
: The new memory block size, in bytes.
realloc
works like this:
- If
ptr
NULL, thenrealloc
the behavior is equivalent to thatmalloc(size)
it will allocate a newsize
memory block of size bytes and return a pointer to the memory block. - If
size
it is 0ptr
and not NULL, thenrealloc
the behavior is equivalentfree(ptr)
to releasing the previously allocated memory block and returning a NULL pointer. - If
ptr
not NULLsize
and not 0,realloc
an attempt will be made to reallocate previously allocated memory blocks. Several things can happen:- If the size of the previously allocated memory block is greater than or equal to that
size
, no new memory block will be allocated, but a pointer to the original memory block will simply be returned without changing the contents of the original memory block. - If the size of the previously allocated memory block is smaller than that
size
,realloc
an attempt will be made to extend the original memory block to the new size. This may **extend in the available memory space behind the original memory block, and if there is not enough contiguous space to expand, it mayrealloc
reallocate a new memory block in another place and copy the original content to the new memory block. **This means thatrealloc
it is possible to return a new pointer instead of the original pointer, so after using itrealloc
, it is better to assign the returned pointer to the original pointer. - If
realloc
allocation of a new memory block fails, NULL is returned, and the previously allocated memory block remains unchanged.
- If the size of the previously allocated memory block is greater than or equal to that
When using realloc
, special attention should be paid to the following points:
- If
realloc
it returns NULL, it means that the reallocation failed, and the original pointer is still valid. To avoid memory leaks, the original pointer should be saved, and the previous memory block should be released as needed. - When used
realloc
, it is best not to modify the raw pointer directly, but torealloc
assign the result of the original pointer to the raw pointer to prevent unexpected memory problems.
Example:
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int *) malloc(40);
if (p == NULL)
return 1;
//使用
int i = 0;
for (i = 0; i < 10; i++) {
*(p + i) = i;
}
for (i = 0; i < 10; i++) {
printf("%d ", *(p + i));
}
//增加空间
// p = (int *)realloc(p, 80); //如果开辟失败的话,p变成了空指针,不能这么写
int *ptr = (int *) realloc(p, 80);
if (ptr != NULL) {
p = ptr;
ptr = NULL;
}
//当realloc开辟失败的时候,返回的也是空指针
//使用
for (i = 10; i < 20; i++) {
*(p + i) = i;
}
for (i = 10; i < 20; i++) {
printf("%d ", *(p + i));
}
//释放
free(p);
p = NULL;
return 0;
}
//输出结果:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
2. Common dynamic memory errors
2.1 Dereference operation on NULL pointer
#include <stdio.h>
#include <stdlib.h>
int main() {
int* p = (int*)malloc(20);
*p = 5; //错误,空指针解引用
//为了不对空指针解引用 需要进行判断
if (p == NULL) {
perror("malloc");
return 1;
}
else {
*p = 5;
}
free(p);
p = NULL;
return 0;
}
2.2 Out-of-bounds access to dynamically allocated spaces
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int *) malloc(20);
if (p == NULL)
return 1;
int i = 0;
for (i = 0; i < 20; i++)//越界访问 20个字节 只能访问5个整型
{
*(p + i) = i;
}
free(p);
p = NULL;
return 0;
}
2.3 Use free to release non-dynamic memory
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 10;
int* p = &a;
free(p);// ok?
return 0;
}
The compiler will directly report an error
2.4 Use free to release part of a dynamically allocated memory
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = (int *) malloc(40);
if (p = NULL)
return 1;
int i = 0;
for (i = 0; i < 5; i++) {
*p = i;
p++;
}
//释放
//在释放的时候,p指向的不再是动态内存空间的起始位置
free(p);// p不再指向动态内存的起始位置
p++;
return 0;
}
2.5 Multiple releases of the same dynamic memory
#include <stdio.h>
#include <stdlib.h>
int main() {
int* p = (int*)malloc(40);
if (p == NULL)
return 1;
int i = 0;
for (i = 0; i < 5; i++) {
*(p + i) = i;
}
//重复free
free(p);
p = NULL;//如果将p赋值为NULL 就可以在free,否则编译器会直接报错
free(p);
return 0;
}
2.6 Dynamically open up memory and forget to release it (memory leak)
#include <stdio.h>
#include <stdlib.h>
int *get_memory() {
int *p = (int *) malloc(40);
return p;
}
int main() {
int *ptr = get_memory();
//使用
//释放 如果不释放 就会导致内存泄漏
free(ptr);
return 0;
}
3. Memory allocation of C/C++ program
Several areas of C/C++ program memory allocation:
- Stack area (stack): When a function is executed, the storage units of local variables in the function can be created on the stack, and these storage units are automatically released when the function execution ends. The stack memory allocation operation is built into the instruction set of the processor, which is very efficient, but the allocated memory capacity is limited. The stack area mainly stores local variables allocated by running functions, function parameters, return data, return address, etc.
- Heap area (heap): Generally allocated and released by the programmer, if the programmer does not release it, it may be recovered by the OS at the end of the program. The allocation method is similar to a linked list.
- The data segment (static area) (static) stores global variables and static data. Released by the system after the program ends.
- Code segment: store the binary code of the function body (class member functions and global functions).
Ordinary local variables are allocated space in the stack area. The feature of the stack area is that the variables created above are destroyed when they go out of scope. However, variables modified by static are stored in the data segment (static area). The characteristic of the data segment is that the variables created on it are not destroyed until the end of the program, so the life cycle becomes longer.
4. Classic written test questions
4.1 Topic 1
#include <stdio.h>
#include <stdlib.h>
void GetMemory(char *p) {
p = (char *) malloc(100);
}
void Test(void) {
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
int main() {
Test();
return 0;
}
What is the result of running the Test function?
Running Test
the function results in undefined behavior.
In GetMemory
the function, a local variable is passed in char *p
, and when it is modified inside the function, it will not affect the pointer in the original calling function. This is because the parameters of the function are passed by value, that is, the function gets a copy of the actual parameter, and the modification of the parameter will not affect the original actual parameter.
In Test
the function, a NULL pointer is str
passed to GetMemory
the function, then GetMemory
the memory is allocated in the function and the new address is assigned to p
. But this str
has no effect on it, str
it is still a NULL pointer, pointing to unallocated memory.
Then, Test
using in the function strcpy
to copy the string to str
the memory pointed to, but str
the memory pointed to has not been allocated, which will lead to undefined behavior.
In order to correctly allocate memory and use pointers, it is necessary to modify GetMemory
the function so that it returns the allocated memory address and Test
receive the returned pointer in the function. free
Also, don't forget that you need to use functions to release dynamically allocated memory after you're done using it .
Rewrite 1:
#include <stdio.h>
#include <stdlib.h>
void GetMemory(char **p) {
*p = (char *) malloc(100);
}
void Test(void) {
char *str = NULL;
GetMemory(&str); //传指针的地址
strcpy(str, "hello world");
printf(str);
//释放
free(str);
str = NULL;
}
int main() {
Test();
return 0;
}
Rewrite 2:
#include <stdio.h>
#include <stdlib.h>
char *GetMemory() {
char *p = (char *) malloc(100);
return p;
}
void Test(void) {
char *str = NULL;
str = GetMemory(); //接受返回的p
strcpy(str, "hello world");
printf(str);
//释放
free(str);
str = NULL;
}
int main() {
Test();
return 0;
}
4.2 Topic 2
char *GetMemory(void) {
char p[] = "hello world";
return p;
}
void Test(void) {
char *str = NULL;
str = GetMemory();
printf(str);
}
What is the result of running the Test function?
In GetMemory
the function, a local array is defined char p[] = "hello world";
, and the address of the array is returned to the caller. However, once GetMemory
the function finishes executing, its local variable ( p
array) will be destroyed because it is a local variable of automatic storage class. Therefore, the returned pointer points to memory that is no longer valid.
In Test
the function, you GetMemory
assign the return value of to the pointer str
, and then printf
print str
what is pointed to using . Since GetMemory
an invalid pointer (pointing to a local array that has been destroyed) is returned, printf
garbage values may be printed, or the program may crash, or cause other unpredictable results.
This problem is known as the "dangling pointer" problem, because pointers hang around pointing to memory locations that are no longer valid.
To solve this problem, you can consider using dynamic memory allocation to allocate memory for storing strings, and remember to use to release the memory after use free
.
Modified code example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *GetMemory(void) {
char *p = (char *)malloc(strlen("hello world") + 1);
if (p != NULL) {
strcpy(p, "hello world");
}
return p;
}
void Test(void) {
char *str = NULL;
str = GetMemory();
if (str != NULL) {
printf("%s\n", str);
free(str); // 释放内存
}
}
int main() {
Test();
return 0;
}
4.3 Topic 3
void GetMemory(char **p, int num) {
*p = (char *) malloc(num);
}
void Test(void) {
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
What is the result of running the Test function?
The memory is not freed, resulting in a memory leak
Modified code example:
void GetMemory(char **p, int num) {
*p = (char *)malloc(num);
}
void Test(void) {
char *str = NULL;
GetMemory(&str, 100);
if (str != NULL) {
strcpy(str, "hello");
printf("%s\n", str);
free(str); // 释放内存
}
}
4.4 Topic 4
void Test(void) {
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL) {
strcpy(str, "world");
printf(str);
}
}
What is the result of running the Test function?
str is released early, accessing str again will lead to wild pointer behavior