C语言动态内存管理函数(malloc、calloc、realloc 和 free)

动态内存管理函数以及常见错误

  • malloc 函数
  • calloc 函数
  • realloc 函数
  • free 函数
  • 常见的内存错误

在C语言中,如果开辟了一个数组,那么他的大小也就基本确定了,此时无论增加数据或减少数据,数组的大小是不会变的。如果我们要用数组存取的内容数量是不确定的,或多或少,那我们就不能灵活的使用内存,数组创建大了浪费,创建小了不够用。所以,C语言提供了几个内存管理函数,能够有效解决上面的问题,可以做到用多少开辟多少。

malloc 函数

void *malloc( size_t size );
在内存中分配 size 个字节的空间。
返回值:malloc 返回指向已分配空间的空指针,如果可用内存不足则返回 NULL。若要返回指向非 void 类型的指针,请对返回值使用类型强制转换。

使用非常简单:使用时需要引入 或者 ,包括下面的 calloc 函数和 realloc 函数。

int* pi=malloc(10);		//int型指针接收,每次访问4个字节
chat* pc=malloc(10);    //char型指针接收,每次访问1个字节

但,一般不会这么使用。更多是这样使用的:

//分配10个int型大小空间,并强转换成int*,并用int*指针接收。
int* pi=(int*)malloc(10*sizeof(int));	
char* pi=(int*)malloc(10*sizeof(char));	

calloc 函数

void *calloc( size_t num, size_t size );
calloc函数为num元素数组分配存储空间,每个元素的长度为字节。每个元素都初始化为0。
calloc 和 malloc 很相似,只不过 calloc 会初始化为0。
如果不希望初始化,可以使用 malloc 函数,如果要初始化,则使用 calloc。

用 calloc 分配10个 int 大小的空间:

int* pt = (int*)calloc(10, sizeof(int));	//10个int的字节空空会被初始化成0;

realloc 函数

void *realloc( void *memblock, size_t size );
重新分配内存块。
memblock:指向先前分配的内存块的指针。
size:新的大小。
realloc 函数的作用是改变已分配内存块的大小。memblock 参数指向内存块的开头。
如果 memblock 为 NULL, realloc 就与 malloc 相同,并分配一个大小为 size 字节的新内存。
如果 memblock 不为 NULL,那么它应该是由之前调用 calloc、malloc 或 realloc 返回的指针。
注意: 如果新分配的空间比一开始分配的空间大,系统会在一开始分配的空间后面接着找一块内存接上,如果不够,就会重新找一块内存,包括前面已分配的内存。

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	int size = _msize(p);	//_msize函数返回在堆中分配的内存块的大小。
	printf("%u\n", size);
	int* pt= (int*)realloc(p, 20 * sizeof(int));//重新分配20个int大小
	size = _msize(p);
	printf("%u\n", size);
	printf("%p\n", p);
	printf("%p\n", pt);
	return 0;
}

运行结果:
C语言动态内存管理函数(malloc、calloc、realloc 和 free)_第1张图片

补充:_msize 函数需引入 头文件。

free 函数

void free( void *memblock );
释放以前分配的内存块。
free 函数注意:

  1. free 函数释放先前通过调用 calloc、malloc 或 realloc 分配的内存块(memblock)。
  2. 释放的字节数等价于分配块(或在 realloc 情况下重新分配)时请求的字节数。
  3. 如果 memblock 为 NULL,则忽略指针并立即返回 free。
  4. 试图释放无效指针(指向未由 calloc、malloc 或 realloc 分配的内存块的指针)可能会影响后续的分配请求并导致错误。

下面是一个 free 函数的错误使用:

指针 p 并不是指向由 calloc、malloc 或者 realloc 分配的内存,释放它就会发生错误。

C语言动态内存管理函数(malloc、calloc、realloc 和 free)_第2张图片


正确使用方法:
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	free(p);	//释放指针p;
	p = NULL;	//手动置成NULL
	return 0;
}

这里把指针 p 释放后(把申请的空间还给系统),需要手动将 p 指向的内存地址置成NULL(不手动置成 null 的话,p 还是指向刚刚申请的内存地址,通过 p 还是可以访问到释放过后的内存,造成非法访问之类问题)。

前面3个内存申请函数,在申请失败的时候,都会返回空指针,所以当我们使用这3个函数申请内存后,需要判断是否申请成功,也就是看指针是否指向 NULL。

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		//如果申请失败,打印错误信息
		perror("malloc:");	//如果上面申请的内存足够大,这里就会报内存不足。
	}
	else
	{
		//申请成功。再对申请后的内存做处理
	}
	//使用完成后,释放指针所指向的内存
	free(p);	
	p = NULL;
	return 0;
}

常见的内存错误

1. 对空指针解引用:
在下面这个程序中,如果申请空间失败,函数返回空指针,那在 for 循环里对空指针解引用就会发生错误。

//错误使用,没有判断申请成功没有
int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	free(p);	
	p = NULL;
	return 0;
}

正确使用时,应先判断是否申请成功:

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

2. 对动态开辟的内存越界访问:

比如申请了5个int字节,但使用了10个int,就会造成越界访问。

C语言动态内存管理函数(malloc、calloc、realloc 和 free)_第3张图片

避免以上问题只需做到,开辟多少,使用多少。

3. 使用 free 释放了非动态开辟的内存:

int main()
{
	int a = 10;
	int* p = &a;	
	free(p);  	//错误,释放了非动态开辟的内存
	p = NULL;
	return 0;
}

4. 没有完全释放开辟的内存:

int main()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc:");
	}
	else
	{
		for ( i = 0; i < 10; i++)
		{
			*p++ = i;	//可以使用*(p+i)=i, 这样就不会改变p的值。
		}
	}
	free(p);   	//错误,在for循环里指针p的值已经改变
	p = NULL;
	return 0;
}

5. 多次 free 同一块开辟的内存:

int main()
{
	int i = 0;
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		perror("malloc:");
	}
	else
	{
		for (i = 0; i < 10; i++)
		{
			*(p+i) = i;
			printf("%d ", *(p + i));
		}
	}
	free(p);	
	free(p);	//报错,对同一块内存释放了两次。
	p = NULL;
	return 0;
}

但是当我们把 p=NULL; 和第二个 free( p); 交换一下位置,程序就不会报错,这是因为此时的 p 已经指向空指针了。

6. malloc 后没有 free:
程序只管 malloc ,但是没有 free,最后就会一直申请内存,导致内存不够,程序挂了。
所以使用 malloc 等函数后,一定要 free,最好是成对书写。

使用 malloc、calloc 和 realloc 函数一定要注意以上几个问题,避免一些不必要的错误。

你可能感兴趣的:(C语言,c语言,算法,开发语言)