【C语言】动态内存函数的使用

1、为什么存在动态内存分配

原因1:对于空间的需求会增大。
        以数组为例: char arr[ ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        数组在声明长度是固定的,内存中开辟的空间大小也固定。如果数组的空间不能满足我们的
        需求,就需要使用动态内存分配

原因2:有些时候需要的空间大小在程序运行的时候我们才能知道。
        这时数组编译时开辟空间的方式就不能满足需求。

原因3:可以调节内存的分配
        
如果一个数组开辟的空间过于大,但实际上用不了这么多,就需要使用动态内存分配缩减空
        间的占用

2、动态内存函数的介绍

 2.1 malloc

 动态内存开辟函数malloc:

void* malloc(size_t size);

功能:分配size字节大小的内存块,返回指向块开头的指针。 

  • 新分配的内存块的内容没有初始化,保留不确定的值。
  • 分配成功,返回指向函数分配的内存块的指针。
  • 分配不成功,则返回空指针。
  • 返回值是void* ,具体类型由使用者自己决定
  • 如果size为0,则返回值取决于特定的库实现(它可能是空指针,也可能不是空指针),但返回的指针不应被解引用。

 2.2 free

动态内存释放和回收函数free:

 void free(void* ptr);

功能:以前通过调用malloc、calloc或realloc分配的内存块将被释放,使其可以再次用于下一次分
           配。 

  • 如果ptr不指向用上述函数分配的内存块,则会导致未定义的行为。
  • 如果ptr是空指针,则函数不执行任何操作。
  • free函数使用后不会改变ptr本身的值,因此它仍然指向之前的位置,只是指向位置中的内容被释放,此时ptr 是野指针。
  • 使用free函数后应及时将ptr赋为空,防止野指针的使用。
  • 如果ptr不指向先前用malloc、calloc或realloc分配的内存块,并且不是空指针,则会导致未定义的行为。
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* arr = (int*)malloc(sizeof(int) * 5); //为arr开辟5个int型空间
	if (NULL == arr)//判断释放开辟成功
	{
		perror("malloc");//开辟失败则返回原因
        return -1;
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			*(arr + i) = i;
		}
		for (int i = 0; i < 5; i++)
		{
			printf("%d ", *(arr + i));
		}
        //释放内存
        free(arr);
        arr = NULL;//是否有必要?
	    return 0;
    }
}

  2.3 calloc

void* calloc(size_t num, size_t size);

功能:为包含 num 个 size 大小元素的数组分配内存空间,并将内存中每个字节初始化为0 

  • calloc函数的有效结果是分配了一个0初始化的(num*size)字节的内存块。
  • 分配成功,返回指向函数分配的内存块的指针。
  • 分配不成功,则返回空指针。
  • 如果size为0,则返回值取决于特定的库实现(它可能是空指针,也可能不是空指针),但返回的指针不应被解引用。
#include <stdio.h>
#include <stdlib.h>
int main()
{
	//开辟内存
	int* arr = (int*)calloc(5, sizeof(int));//分配空间,并初始化为0
	if (arr == NULL)
	{
		perror("malloc");
        return -1;
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			printf("%d ", *(arr + i));
		}
		//释放内存
		free(arr);
		arr = NULL;
		return 0;
	}
}

 calloc和malloc 的对比:

  1. 参数不一样
  2. 都是在堆区上申请内存空间,但是malloc不初始化,calloc会初始化为0。
    如果要初始化,就使用calloc,不需要初始化,则使用malloc。

 2.4 realloc

void* realloc(void* ptr, size_t size);

功能: 重新分配内存块——改变ptr所指向的内存块的大小。

  • 重新分配成功,返回指向分配后内存块的指针,它可以是原本 ptr 指向的位置 ,也可以是一个新的位置
  • 重新分配失败,在C99中,空指针表示函数分配存储失败,因此 ptr 所指向的内存块没有被修改。
  • 如果size为0,则返回值取决于特定的库实现:它可能是空指针,也可能是不能解引用的其他位置。
  • 如果传给realloc的是一个空指针,则realloc功能与malloc相同

新大小大于旧大小,且重新分配成功有两种情况

情况1:原本的 ptr 所指向的内存块之后有足够大的空间,于是往后继续分配空间,总空间大 
             小为 size 字节,返回的地址不变
情况2:原本的 ptr 所指向的内存块之后没有足够大的空间,编译器找到一个新的足够大的空
             间,将它作为新的内存块,还会将原来内存中的数据移动到 新 的空间释放旧空间
             (旧指针不为空),返回新空间的地址

int main()
{
	//开辟内存
	int* arr = (int*)calloc(5, sizeof(int));
	if (arr == NULL)
	{
		perror("malloc");
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			*(arr + i) = i;
			printf("%d ", *(arr + i));
		}
	}
	//重新分配内存
	int* ptr = (int*)realloc(arr, sizeof(int) * 1000);
	if (ptr != NULL)
	{
		if (ptr == arr)
		{
			printf("情况1\n");
		}
		else
		{
			printf("情况2\n");
		}
		arr = ptr;
		for (int i = 5; i < 1000; i++)
		{
			*(arr + i) = i;
		}
		for (int i = 0; i < 1000; i++)
		{
			printf("%d ", *(arr + i));
			if ((i+1) % 20 == 0)
			{
				printf("\n");
			}
		}
	}
	//释放内存
	free(arr);
	arr = NULL;
	return 0;
}

3、常见的动态内存错误

 3.1对NULL指针的解引用操作

对空指针解引用操作的危害:转载:【缺陷周话】第1期:空指针解引用_chongzhao2789的博客-CSDN博客

void test ()
{
int * p = ( int * ) malloc ( INT_MAX / 4 );
* p = 20 ; // 如果 p 的值是 NULL ,就会有问题
free ( p );
p = NULL;
}
可能出现的问题:结果未定义

 3.2 对动态开辟空间的越界访问

void test ()
{
int i = 0 ;
int * p = ( int * ) malloc ( sizeof ( int 10 );
if ( NULL == p )
{
perror("malloc");
}
for ( i = 0 ; i <= 10 ; i ++ )
{
* ( p + i ) = i ; // i 10 的时候越界访问,程序可能会崩溃
}
free ( p );
p = NULL;
}
可能出现的问题:程序崩溃

  3.3 对非动态开辟内存使用free释放

void test ()
{
int a = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int * p = & a ;
free ( p ); //ok?
}
可能出现的问题: 结果未定义

3.4 使用free释放一块动态开辟内存的一部分 

void test ()
{
int * p = ( int * ) malloc ( 100 );
if ( NULL == p )
{
perror("malloc");
}
for ( i = 0 ; i < 10 ; i ++ )
{
* p   = i ;
p ++ ;
}
free ( p ); //p 不再指向动态内存的起始位置
}
可能出现的问题:程序崩溃

 3.5 对同一块动态内存多次释放

void test ()
{
int * p = ( int * ) malloc ( 100 );
if ( NULL == p )
{
perror("malloc");
}
free ( p );
/*一系列操作*/
free ( p ); // 重复释放
}
可能出现的问题:程序崩溃
如果 free (p )之后直接将p赋为空指针,那么再次释放将不会出现问题

 3.6 动态开辟内存忘记释放(内存泄漏)

void test ()
{
int * p = ( int * ) malloc ( 100 );
if ( NULL != p )
{
* p = 20 ;
}
}
int main ()
{
test ();
while ( 1 );
}
malloc  calloc  realloc 所申请的空间,如果不想使用,需要free 释放
如果不使用 free 释放 ,程序结束之后,会由操作系统回收。
如果不使用 free 释放,程序也不结束,将发生内存泄露

总结:动态开辟的空间一定要释放,并且正确释放 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值