Detailed explanation of C language - dynamic memory allocation (2)

Table of contents

Foreword:

Several classic example questions

Example 1:

Example 2:

Example three:

Example 4:

Example five: 

 Memory allocation for C/C++ programs

flexible array

Features of flexible arrays:

Use of flexible arrays: 

Flexible array replacement:

Advantages of flexible arrays:

summary:

Foreword:

I hope that after reviewing Dynamic Memory Allocation (1) , reading this article will further improve your abilities!

Several classic example questions

Example 1:

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

There is no result after running and it exits abnormally: 

We can see that an exception occurs in the program during debugging

  • First, the main function calls the Test function. The Test function declares a char* type variable str and assigns it a value of NULL.
  • Then call the GetMemory function, passing str as a parameter. The formal parameter p just copies the value of the actual parameter str. The two are independent. In the function, p dynamically allocates 100 bytes of space, which has nothing to do with str. So after calling the GetMemory function, str is still a null pointer.
  • The parameter of strcpy is a pointer type. The pointer type parameter needs to be dereferenced internally in strcpy. As a                                               parameter, str will cause the null pointer to be dereferenced, resulting in illegal access to the null pointer.

This procedure causes two problems:

  1. If the NULL pointer is dereferenced, the program will crash.
  2. No space is released, causing memory leak problems.

Therefore we make the following changes: 

If you want to modify a pointer variable, you need to pass the address of the pointer variable into the function. The parameters of the function are received with a secondary pointer, so that space can be dynamically allocated within the function.

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

 Output result:

Example 2:

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

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

Output result: 

In the GetMemory function, p stores the first address of "hello world" and returns p. When the GetMemory function is called, although str obtains the address stored in p, the space of p will be destroyed and p becomes a wild pointer. str What is obtained is the wild pointer. Equivalent to illegal access.

 If you add static to p and declare p as a static variable to extend the life cycle, you can output "hello world".

Example three:

int* test()
{
	int a = 10;
	return &a;
}

int main()
{
	int* p = test();
	printf("%d\n", *p);

	return 0;
}

Output result:

Once testthe function returns when calling the test function, athe space of the variable will not be deleted immediately, but its life cycle ends and the memory space can be reused by the system, that is, the memory space may be allocated to subsequent function calls or variables. , the memory may be overwritten by other data.

This time it may be just a fluke that the space of a is not occupied. If you add such an output statement before the output statement,

printf("hehe");

The space of a is occupied, and the output result will no longer be 10.

 Output result:

Example 4:

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

Output result: 

There seems to be no problem, but there is still the only problem - forgetting to release the memory. 

	free(str);
	str = NULL;

Example five: 

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

 After free processes str, str becomes a wild pointer, so it is judged not to be empty and is copied. However, the space pointed by str has been reclaimed by free. Any further copying will cause illegal memory access.

In fact, after free is released, str should be assigned to NULL in time.

 Memory allocation for C/C++ programs

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 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) 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).
Now we can better understand the example of modifying local variables with the static keyword:
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.

flexible array

Maybe you have never heard of the concept of flexible array , but it does 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.
If the first compiler doesn't support it, use the second one.
struct s
{
	int n;
	int arr[];//柔性数组
};

struct s
{
	int n;
	int arr[0];//柔性数组
};

Features of flexible arrays:

  • Flexible array members in a structure must be preceded by at least one other member.
  • The size of this structure returned by sizeof does not include the memory of the flex array.
  • The structure containing flexible array members uses the malloc () function to dynamically allocate memory, and the allocated memory should be larger than the size to accommodate the expected size of the flexible array.

Use of flexible arrays: 

struct s
{
	int n;
	int arr[0];
};

int main()
{
	//printf("%d\n", sizeof(struct S));
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	ps->n = 100;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	free(ps);
	ps = NULL;

	return 0;
}

 If you need to increase the capacity, add the realloc function to increase the capacity:

int main()
{
	//printf("%d\n", sizeof(struct S));
	struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
	if (ps == NULL)
	{
		perror("malloc");
		return 1;
	}
	ps->n = 100;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	//空间不够,需要增容
	struct S* ptr = realloc(ps, sizeof(struct S) + 60);
	if (ptr == NULL)
	{
		perror("realloc");
		return 1;
	}
	ps = ptr;
	ps->n = 15;
	for (i = 0; i < 15; i++)
	{
		printf("%d\n", ps->arr[i]);
	}

	//释放
	free(ps);
	ps = NULL;

	return 0;
}

Flexible array replacement:

We can use pointer type structure members instead of flexible arrays: 

struct S
{
	int n;
	int* arr;
};


int main()
{
	struct S* ps = (struct S*)malloc(sizeof(struct S));
	if (ps == NULL)
	{
		perror("malloc->ps");
		return 1;
	}
	ps->n = 100;
	ps->arr = (int*)malloc(40);//1 2 3 4 5 6 7 8 9 10
	if (ps->arr == NULL)
	{
		perror("malloc->arr");
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		ps->arr[i] = i + 1;
	}

	//调整
	int* ptr = (int*)realloc(ps->arr, 60);
	if (ptr != NULL)
	{
		ps->arr = ptr;
	}
	else
	{
		perror("realloc");
		return 1;
	}
	//打印
	for (i = 0; i < 15; i++)
	{
		printf("%d\n", ps->arr[i]);
	}

	//释放
	free(ps->arr);
	ps->arr = NULL;

	free(ps);
	ps = NULL;

	return 0;
}

Advantages of flexible arrays:

The first and second methods above can accomplish the same function, but the implementation of the first method has two benefits:      
The first benefit is: 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.
The second benefit is: 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)

summary:

 The road to learning is long and difficult. I hope you will keep reviewing and coding. You will definitely receive your favorite offers in the future! ! !

Guess you like

Origin blog.csdn.net/m0_73800602/article/details/133237853