剖析c语言动态内存管理

Posted AURORA_CODE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剖析c语言动态内存管理相关的知识,希望对你有一定的参考价值。

1. 动态内存分配存在的意义

在之前我们所掌握的内存开辟方式有:

int val = 20;//在栈空间上开辟四个字节
char arr[10] = {0};//在栈空间上开辟10个字节的连续空间

上述开辟方式有俩个特点:

  1. 开辟的空间大小是固定的。
  2. 数组在声明的时候必须确定数组的长度。
    然而动态内存就可以避免这些问题。
    动态内存分配(Dynamic Memory Allocation)就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不象数组等静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

2. 动态内存函数

2.1 malloc

void* malloc (size_t size);

该函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。返回值的类型是 void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
  • 如果参数 size为0,malloc的行为是标准是未定义的,取决于编译器。
    c语言专门定义了一个free函数,用来对开辟的动态内存进行释放和回收。
void free (void* ptr);

注意

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
    例:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
	int num;
	printf("please input arr's size:>");
	scanf("%d", &num);
	int *arr = (int*)malloc(sizeof(int)*num);//在堆区申请一块空间。
	assert(arr != NULL);
	for (int i = 0; i < num; i++)
	{
		*(arr + i) = i;
	}
	for (int i = 0; i < num; i++)
	{
		printf("%d  ",*(arr + i));
	}
	free(arr);//释放在堆区开辟的空间。
	arr=NULL;
	return 0;
}

2.2 calloc

void* calloc (size_t num, size_t size);
  • 函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
    例:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
	int num;
	printf("please input arr's size:>");
	scanf("%d", &num);
	int *arr = (int*)calloc(num, sizeof(int));
	assert(arr != NULL);
	for (int i = 0; i < num; i++)
	{
		printf("%d  ", *(arr + i));
	}
	free(arr);
	arr = NULL;
	return 0;
}

2.3 realloc

void* realloc (void* ptr, size_t size);
  • realloc函数的出现让动态内存管理更加灵活。
  • 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时候内存,我们一定会对内存的大小做灵活的调整。那
    realloc 函数就可以做到对动态开辟内存大小的调整
    例:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int main()
{
	int num;
	printf("please input arr's size:>");
	scanf("%d", &num);
	int *arr = (int*)malloc(sizeof(int)*num);
	assert(arr != NULL);
	for (int i = 0; i < num; i++)
	{
		*(arr + i) = i;
	}
	printf("Please enter the size of the expanded array:>");
	int num2;
	scanf("%d", &num2);
	arr = realloc(arr, num2 * sizeof(int));
	assert(arr != NULL);
	for (int i = num; i < num2; i++)
	{
		*(arr + i) = i;
	}
	for (int i = 0; i < num2; i++)
	{
		printf("%d  ", *(arr + i));
	}
	free(arr);
	return 0;
}

3. 常见的动态内存错误

  • 对NULL指针进行解引用操作
void test()
{
	int *p=(int*)malloc(64);
	*p=20;//如果动态内存开辟失败p=NULL,就会出问题
	free(p);
}
  • 对动态开辟空间的越界访问
void test()
{
	int *p=(int)malloc(sizeof(int)*20);
	if(p!=NULL)
	{
		for(int i=0;i<=20;i++)
		{
			*(p+i)=i;//当i=20时,越界访问。
		}
	}
	free(p);
}
  • 对非动态开辟空间使用free
void test()
{
	int a=20;
	int* p=&a;
	free(p);//free无法对非动态开辟的空间进行释放。
}
  • free释放一块动态开辟内存的一部分
void test()
{
	int *arr=(int*)malloc(sizeof(int)*20);
	arr=arr+5;
	free(arr);
}
  • 对动态开辟的同一块内存进行多次释放
void test()
{
	int *arr=(int*)malloc(sizeof(int)*20);
	free(arr);
	free(arr);//重复释放
}
  • 开辟的动态内存没有进行释放(内存泄漏)
void test()
{
	int *arr=(int*)malloc(sizeof(int)*20);
	//开辟的动态内存使用完后没有进行释放,导致内存泄漏。
}

4. 柔性数组

4.1 柔性数组的特点

  • 结构中的柔性数组成员前面必须至少一个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
    例:
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
printf("%d\\n", sizeof(type_a));//输出的是4

4.2 柔性数组的使用

代码一:

#include<stdio.h>
#include<assert.h>
typedef struct st_type
{
int i;
int a[0];//柔性数组成员
}type_a;
int main()
{
	int i = 0;
	type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
	assert(p!=NULL);
	p->i = 100;
	for(i=0; i<100; i++)
	{
		p->a[i] = i;
	}
	free(p);
}

4.3 柔性数组的优势

代码二:

#include<stdio.h>
typedef struct st_type
{
int i;
int *p_a;
}type_a;
int main()
{
	type_a *p = malloc(sizeof(type_a));
	p->i = 100;
	p->p_a = (int *)malloc(p->i*sizeof(int));
	//业务处理
	for(i=0; i<100; i++)
	{
	p->p_a[i] = i;
	}
	//释放空间
	free(p->p_a);
	p->p_a = NULL;
	free(p);
	p = NULL;
}

代码1与代码2实现的功能相同,但相比之下,代码1有以下俩个好处:
第一个好处是:方便释放内存;
第二个好处是:有利于提高访问速度。

以上是关于剖析c语言动态内存管理的主要内容,如果未能解决你的问题,请参考以下文章

剖析C动态内存管理 (malloc,calloc,realloc,柔性数组)

C++动态内存管理与源码剖析

玩转C语言动态内存,轻松管理动态内存空间

玩转C语言动态内存,轻松管理动态内存空间

C语言进阶:动态内存管理

C语言之动态内存管理(动态内存分配+经典笔试题+柔性数组)[建议收藏]