Dynamic memory allocation review

Why is there a dynamic memory allocation?

int val = 20; // Open up 4 bytes on the stack space

char arr[10] = {0 }; //Open up 10 bytes of continuous space on the stack space

However, there are two characteristics of the above method of opening up space:
1. The size of the space opened up is fixed (too limited)
2. When the array is, the length of the array must be specified, and the memory it needs is allocated at compile time .

However, the demand for space is not just the above-mentioned situation. Sometimes the size of the space we need is only known when the program is running, and the way the array opens up space at compile time cannot be satisfied. So at this time we need to introduce dynamic memory development.

malloc
#include<stdlib.h> //malloc header file
void* malloc(size_t size); // declaration (() is the total size)

#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
    
    
	//向内存申请10个整形的空间
	int* p = (int*)malloc(10 * sizeof(int)); //当申请成功的时候就会返回申请到的起始地址
	if (p == NULL) // 但也有一定的可能会申请失败,就会返回一个空指针
	{
    
    
		//打印错误的原因
		printf("%s\n", strerror(errno));
	}
	//  当动态申请的空间不再使用的时候
   //就应该还给操作系统,引入了free这个函数。
    free(p);
    p = NULL;
	return 0;
}

calloc
void* calloc(size_t num,size_t size); //Declaration
1. The function of the function is to open up a space for num elements of size size, and initialize each byte of the space to 0.
2. With malloc The difference is that calloc will initialize each byte of the requested space to 0 before returning the address.

#include<stdlib.h>
#include<string.h>
#include<errno.h>
int main()
{
    
    
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		printf("%s\n", strerror(errno));
	}
}

realloc
1. The emergence of the realloc function makes dynamic memory management more flexible
. 2. Sometimes we find that the space applied for in the past is too small, and sometimes we feel that the applied space is too large, so for reasonable memory, we will definitely treat the memory The size can be adjusted flexibly.
void* realloc(void* ptr, size_t size); // Prototype
Among them:
1.ptr is the memory address to be adjusted 2. The
new size after size
adjustment 3. The return value is the adjusted memory starting position
4. This function On the basis of adjusting the size of the original memory space, the data in the original memory will also be moved to the new space.
5. There are two situations when realloc adjusts the memory space: if the space behind realloc is large enough, it will directly append and then return The original old address, but when the space behind is not large enough, you need to reconsider taking a large space for development, copy the original data, release the old memory space, and then return to the newly opened address.
int* p = realloc(NULL,40); // At this time, this function is equivalent to malloc(40).
Note: Use a new pointer to receive the address returned by realloc during development

    int* ptr = realloc(p, INT_MAX);
    if (ptr != NULL)
    {
    
    
		p = ptr;
     }
    // realloc也要记住释放空间
    free(p);
    p = NULL;

Common dynamic memory errors
1. Dereference the null pointer (you must judge the pointer created by it)

int main()
{
    
    
	int* p = (int*)malloc(40);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

For this code, you didn't judge p. If he fails to open up the memory, it will return a null pointer, and then an error will be reported.
2. Cross-border access

int main()
{
    
    
	int* p = (int*)malloc(5*sizeof(int));
	if (p == NULL)
	{
    
    
		return 0;
	}
	else 
	{
    
    
		int i = 0;
		for (i = 0; i < 10; i++)
		{
    
    
			*(p + i) = i;
		}
	}
	free(p);
	p = NULL;
	return 0;
}

3. Use free to release non-dynamically opened memory (free is the space of the heap area)

int main()
{
    
    
	int a = 10; // 这里是局部变量,是栈区的空间
	int* p = &a;
	*p = 20;
	free(p);
	p = NULL;
	return 0;
}

4. Use free to release part of
the dynamically opened memory. This program will crash, because your p keeps changing, and finally points to the next location of the last address opened. You are releasing it, and the space behind does not belong to it. your.

int main()
{
    
    
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
    
    
		return 0;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*p++ = i; // 把这里改成p+下标解引用来进行赋值,就会好
	}
	free(p);
	p = NULL;
	return 0;
}

5. Multiple releases of the same dynamic memory

int main()
{
    
    
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
    
    
		return 0;
	}
	free(p);
	// ....
	free(p);
	p = NULL; // 在每个free后面进行指针赋值为NULL,也可以很好的解决这个错误,因为你一旦被赋值为NULL,那么后面对这个指针的任何操作都是不管用的。
	return 0;
}

6. Forgetting to release the dynamically opened memory (memory leak). The memory
has been opened up, but it is never recovered.

nt main()
{
    
    
	while (1)
	{
    
    
		malloc(1);
	}
	return 0;
}

Classic example
wrong

//运行程序会造成程序的崩溃
//str以值传递的形式给p,p是Getmemory函数的形参,只能函数内部有效,等Getmemory函数返回以后,动态开辟内存尚未释放,并且无法找到,会造成内存泄漏
void GetMemory(char *p)  // p是所创建的形参变量,在这个函数调用完成以后,这个这个形参就会自动的销毁,且无法在找到这个空间
{
    
     
	p = (char *)malloc(100);
} 
void Test(void) 
{
    
     
	char *str = NULL;   
	GetMemory(str);  
	strcpy(str, "hello world");   // 此时的str还是一个NULL
	printf(str);
}
int main()
{
    
    
	test();
	return 0;
}

Corrected the program code: transfer address

void GetMemory(char **p) 
{
    
    
	*p = (char *)malloc(100);
}
void Test(void)
{
    
    
	char *str = NULL;
	GetMemory(&str);
	strcpy(str, "hello world");   // 此时的str还是一个NULL
	printf(str);

	free(str);
	str = NULL;
}
int main()
{
    
    
	test();
	return 0;
}

The second way to correct

//程序可以理解为:p是上司,他所开辟的空间是卧底,在p死掉之前,如果他能告诉别人卧底的身份,那就还会有人知道卧底
char* GetMemory(char *p) 
{
    
     
	p = (char *)malloc(100);
	return p;
} 
void Test(void) 
{
    
     
	char *str = NULL;   
	str = GetMemory(str);  
	strcpy(str, "hello world");   // 此时的str还是一个NULL
	printf(str);
	free(str);
	str = NULL;
}
int main()
{
    
    
	test();
	return 0;
}

The second example:

The problem of returning the stack space address (only the stack area will have such a problem, and the problem of illegal access to memory will not easily occur in the heap area and the static area)

There will be the problem of illegal access to memory. The result is a random value

char *GetMemory(void)
{
    
     
	char p[] = "hello world";   //这里p创建的是一个局部的数组,当除了这个函数内部以后,就会销毁掉,但是他依旧返回了这个创建了字符数组的
	//地址,虽然此时str接受了这个p的地址,但是此时p里面是什么已经没有人知道了,所以结果是随机值
	return p;
} 
void Test(void) 
{
    
     
	char *str = NULL;   
	str = GetMemory(); 
	printf(str);
}
int main()
{
    
    
	Test();
	return 0;
}

Insert picture description here

int* test()
{
    
    
	static int a = 10; // 如果不加static 那么这里所创建的a在栈区的,除了这个函数,就会释放掉,然后你返回了a的地址,但是此时a里面的内容
	//已经被销毁了,所以就会出现非法访问内存的问题。
	//但是因为增加了static是这个变量从栈区变到了静态区,增加了这个局部变量的寿命,遍可以进行后续的修改,也不会报错
	return &a;
}
int main()
{
    
    
	int* p = test();
	*p = 20;
	return 0;
}

The third question:

//一定要有意识:看见malloc 和calloc,realloc就要进行free释放,他们两个是一定一定要成对出现的,不然就会出现内存泄漏的问题
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 mian()
{
    
    
	Test();
	return 0;
}

The fourth question: The
result will print the world, but the problem is very big because the memory is accessed illegally.

void Test(void) 
{
    
     
	char *str = (char *)malloc(100);   
	strcpy(str, "hello");  
	free(str);   
	if (str != NULL)  // 虽然我把str释放掉了,但是释放并不会把str自动的置成空指针。  
	{
    
     
		strcpy(str, "world");//此时这一块空间已经被释放了,你还要对他进行操作,造成了非法访问内存的问题。     
		printf(str);
	}
}
int main()
{
    
    
	Test();
	return 0;
}

Insert picture description here

  1. Stack area (stack): When the function is executed, the storage units of the local variables in the function can be created on the stack, and these storage units are automatically released when the function is executed. The stack memory allocation operation is built into the processor's instruction set and is very efficient, but the allocated memory capacity is limited. The stack area mainly stores local variables, function parameters, return data, return addresses, etc. allocated for running functions.
  2. Heap: Generally allocated and released by the programmer, if the programmer does not release, it may be reclaimed by the OS when the program ends. The allocation method is similar to a linked list.
  3. The data segment (static area) (static) stores global variables and static data. Released by the system after the program ends.
  4. Code segment: Binary code for storing function bodies (class member functions and global functions).

With this picture, we can better understand the example of the static keyword modifying local variables in the "C Language First Understanding".

In fact, ordinary local variables are allocated space in the stack area. The characteristic of the stack area is that the variables created on it are destroyed when they go out of scope.

But the variable modified by static is 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.

In the flexible array
C99, the last element in the structure is allowed to be an array of unknown size, which is called a "flexible array" member.

struct S
{
    
    
	int n;
	int arr[];//结构体中最后一个是未知大小的--柔性数组成员--数组的大小是可以调整的
};
int main()
{
    
    
	//struct S s;
	//printf("%d\n", sizeof(s));// 结果是4,因为在计算柔性数组的大小的时候,是不包含柔性数组成员的
	struct S* ps = (struct S*)malloc(sizeof(struct S)+5*sizeof(int));
	//这里我进行开辟的时候是不知道这个柔型数组成员的大小的,所以我手动的给他设置一个大小,5*sizeof(int)这么大。
	ps->n = 100;
	int i = 0;
	for(i=0;i<5;i++)
	{
    
    
		ps->arr[i] = i;
	}
	struct S* ptr = realloc(ps,44);//此时我认为我原先开辟的柔性数组成员大小不够,我给他调整一下
	if(ptr != NULL)
	{
    
    
		ps = ptr;
	}
	for(i=0;i<10;i++)
	{
    
    
		ps->arr[i] = i;
	}
	//释放
	free(ps);
	ps = NULL;
	return 0;
}

If I just want the array in my structure to be large or small, there is another way.

struct S
{
    
    
	int n;
	int* arr;
};
int main()
{
    
    
	//这里一共使用了两次malloc所以也要进行两次的释放
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	ps->arr = malloc(5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 5; i++)
	{
    
    
		ps->arr[i] = i;
	}
	for (i = 0; i < 5; i++)
	{
    
    
		printf("%d ", ps->arr[i]);
	}
	//调整大小
	int* ptr = realloc(ps->arr, 10 * sizeof(int));
	if (ptr != NULL)
	{
    
    
		ps->arr = ptr;
	}
	for (i = 5; i < 10; i++)
	{
    
    
		ps->arr[i] = i;
	}
	for (i = 5; i < 10; i++)
	{
    
    
		printf("%s ", ps->arr[i]);
	}
	//释放内存
	free(ps->arr); // 因为这里的ps->arr是包含在ps里面的,如果先释放ps指针,那么就找不到ps->arr这个指针了
	ps->arr = NULL;
	free(ps);
	ps = NULL;
	return 0;
}

The second type is more error-prone from a formal point of view, because you need to use malloc twice for two frees, which is easy to forget and you also need to consider which pointer to release first. If you release a wrong pointer, you will make a mistake. However, the use of flexible arrays only requires one malloc memory development. Every time a malloc is used for memory development, it will cause memory fragmentation. The more used, the more memory fragments will be, and the memory space utilization will be very low. Because it caused a lot of unnecessary memory waste. (When the memory is continuous, the access efficiency is higher and the access speed is faster) (register cache (high-speed buffer) memory hard disk) This is a sequence of CPU access

The first benefit is: convenient memory release

If our code is in a function for others, you do a secondary memory allocation in it and return the entire structure to the user. The user can free the structure by calling free, but the user does not know that the members of this structure also need to be free, so you cannot expect the user to discover this. Therefore, if we allocate the memory of the structure and the memory required by its members at one time, and return a structure pointer to the user, the user can free all the memory by doing a free one.

The second advantage is: This is conducive to access speed

Contiguous memory is good for improving access speed and also good for reducing memory fragmentation.

Guess you like

Origin blog.csdn.net/MEANSWER/article/details/110284285