Dynamic memory operations (2)

Continuing from the previous article http://t.csdn.cn/1ONDq , this time we continue to explain the relevant knowledge about dynamic memory.

1. Common dynamic memory errors

1. Dereference the NULL pointer

#include<stdio.h>
#include<stdlib.h>
#include<limits.h>
int main()
{
	int* p = (int*)malloc(INT_MAX/4);
	*p = 20;//如果没有足够的空间导致p为NULL,就会有问题
	//所以必须对malloc的返回值进行判断
	free(p);
	p = NULL;
	return 0;
}

2. Out-of-bounds access to dynamically opened space

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 11; i++)
	{
		p[i] = 0;
		//原本只申请了十个整型的空间,但却访问十一个整型
		//所以造成越界访问
	}
	free(p);
	p = NULL;
	return 0;
}

3. Use free release for non-dynamically allocated memory


int main()
{
	int a = 0;
	int* p = &a;
	free(p);//p不是动态开辟的空间,不能释放
	p = NULL;
	return 0;
}

4. Use free to release a part of dynamically allocated memory

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = 0;
		p++;
	}
	//p++导致p不再指向这块空间的起始地址
	//所以如果释放p,等于释放这块空间的一部分(后五个整型空间)
	//这样就会出问题
	free(p);
	p = NULL;
	return 0;
}

5. Release the same fast dynamic memory multiple times

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	free(p);
	//。。。。。
	free(p);
	//有时候头脑不清醒就可能释放多次,这样就会出问题
	return 0;
}

6. Forgetting to release memory after dynamically allocating memory (most common)

That is, after we dynamically apply for memory, we finally forget to use free to release it, which will cause a memory leak .

2. Several classic examples about dynamic memory

Example 1. What is the result of running the code?

source code:

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;
}

Question: What will be the result of running this code?

Answer to Example 1:

First, dynamic memory is not released;

Second, this error will occur:

The reason is: because in the Test function, the str pointer is set to null, and then passed as a parameter to the GesMemory function. The formal parameter of the function is received by the pointer p, so the pointer p is also NULL, and then the space is dynamically opened for the pointer p, and the function ends. Go to the strcpy function, but we note that we are opening up space for the pointer p, but the pointer str has not changed. Many partners can't figure it out. Isn't this a call by address? If the p pointer changes, the str pointer should also change?

In fact, it is not the case. We should pay attention to the fact that the parameter is a pointer and is not necessarily a call by address. Here is the assignment between pointers, which should be raised to a higher level at the same time. A second-level pointer is required here to count as a call by address, so the pointer str will not change. It is still NULL. Since it is NULL, there is not enough space to put the second parameter of strcpy, so an error is reported.

Example 2. What is the result of running the code?

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

int main()
{
	Test();
	return 0;
}

Question: What is the result of running this code?

Answer to Example 2:

will produce this result:

"Many friends may think that the starting address p of the string is returned in the GetMemory function, so the pointer str is used to receive and print it in the Test function, so the running result should be a printed string."

But this is not the case. We must pay attention to the life cycle of each variable . The life cycle of the array p is only in the function GetMemory, so when the function returns, the space occupied by the variables inside will be automatically destroyed (released). Since The space of p has been released and assigned to the pointer str, so str is a wild pointer . If str is printed again, it will cause illegal memory access .

This type of problem belongs to: the problem of returning stack space address .

3. Memory allocation for C/C++ programs

As shown below:

Several areas of memory allocation for C/C++ programs:
1. Stack area ( stack ): When executing a function, the storage units of local variables within 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 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 area ( heap ): Generally allocated and released by the programmer. If the programmer does not release it, it may be recycled 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. It is released by the system after the program ends.
4. Code segment: stores the binary code of the function body (class member function and global function).
In fact, ordinary local variables allocate space in the stack area . The characteristic 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 above are not destroyed until the end of the program, so the life cycle becomes longer.

4. Flexible array

(1) The concept of flexible array:

Maybe you've never heard of the concept of flexible arrays , but they do exist.
In C99 , the last element in a structure is allowed to be an array of unknown size. This is called a "flexible array" member.
as follows:
//定义一:
struct S
{
	int a;
	int arr[0];
};
//定义二:
struct B
{
	int a;
	char b;
	int arr[];
};

(2) Characteristics of flexible arrays:

1. The flexible array member in the structure must be preceded by at least one other member.
2. The size of the structure returned by sizeof does not include the memory of the flexible array, that is, sizeof only calculates the size of the front member of the flexible array.
3. Use the malloc () function to dynamically allocate memory for structures containing flexible array members , and the allocated memory should be larger than the size to accommodate the expected size of the flexible array.

(3) Use of flexible arrays:

For example:
struct S
{
	int a;
	int arr[];
};

int main()
{
	//动态开辟了4+40个字节,因为柔性数组是不会被sizeof计算的
	//前面四个字节是给成员a的,后面四十个字节给柔性数组
	//因为柔性数组的大小是未知的,我们只需给出预期大小
	struct S* str = (struct S*)malloc(sizeof(struct S) + 40);
	//检查
	if (str == NULL)
	{
		perror("malloc");
		return 1;
	}
	//使用
	str->a = 10;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		str->arr[i] = i + 1;
		//printf("%d ", str->arr[i]);
	}
	//用realloc扩容,因为柔性数组大小未知,是可以改变的
	//将之前柔性数组的10个字节的大小扩容到15个
	struct S* p = (struct S*)realloc(str, sizeof(struct S) + 60);
	//检查
	if (p == NULL)
	{
		perror("realloc");
		return 1;
	}
	//使用
	str = p;
	for (i = 10; i < 15; i++)
	{
		str->arr[i] = i + 1;
	}
	//打印
	for (i = 0; i < 15; i++)
	{
		printf("%d ", str->arr[i]);
	}
	//释放
	free(str);
	str = NULL;
	return 0;
}

Use the malloc function to open up space and the realloc function to expand the size, so that the size of the array is variable and flexible.

Yes, this is the characteristic of flexible arrays.

(4) Advantages of flexible arrays:

1. Convenient memory release:
If our code is in a function for others to use, you make a secondary memory allocation in it and return the entire structure to the user. The user can release the structure by calling free , but the user does not know that the members in the 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 once, and return a structure pointer to the user, the user can free all the memory once .
2. This is beneficial to access speed:
Contiguous memory is beneficial to improving access speed and reducing memory fragmentation. (Actually, I personally don’t think it’s much higher. Anyway, you can’t run and you have to use offset addition to address).

Guess you like

Origin blog.csdn.net/hffh123/article/details/133349260