C语言问题集——动态内存分配(1)

1、引入:

一般开辟内存空间的方法:

int g_val = 0; //在栈空间上开辟4个byte。

int array [10] = {0}; //在栈空间上开辟40个byte的连续空间。

或者int array [] = {0,1};//在栈空间上开辟8个字节的连续空间。

但是上述开辟空间的方式有两个特点:

1、空间开辟大小是固定的。

2、数组在申明的时候,必须指定数组的长度,或是初始化所需要的元素,它所需要的内存在编译期间分配。

这就意味着我们需要在创建的时候就知道我们需要多少的空间,但是有时候我们所需的空间大小往往是在编写的过程中才知道,或者说需要调整,但是上述的方法显然是不能满足我们的需求的,此时就需要动态内存分配:

首先要了解内存的布局:

内存大致被分为这么几个区域,其中内核空间是用来留给操作系统以及关键组件的,是用户代码不能读写的,栈区主要用来存放局部变量以及函数形参,堆区主要用来动态内存分配,所谓的数据段也就是静态区,主要用来存放static修饰的静态变量和创建的全局变量。

        所以,动态内存是在堆区开辟的。

2、动态内存开辟函数介绍:

<1> malloc和free

        mallo是一个动态内存开辟的函数:

其中参数size是你希望开辟的字节数。

这个函数向内存申请一块连续的空间,并返回指向这块空间的指针。(malloc使用的库是stdlib.h

(1)如果开辟成功,则返回一个指向所开辟空间的指针。(这块空间的首地址)

(2)如果开辟失败,就返回NULL,因此malloc函数的返回值一定要检查。

(3)返回值的类型是void*,所以malloc函数并不知道开辟的空间的类型,具体使用时由使用者自己决定。

(4)如果参数size传入为0,malloc的行为是标准未定义的,取决于编译器。

详细使用样例请看代码:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
	int i = 0;
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p != NULL)
	{
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
	}

	return 0;
}

        

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
int main()
{
	int i = 0;
	int* p = (int*)malloc(INT_MAX);
	if (p != NULL)
	{
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
	}

	return 0;
}

C语言提供了另外一个函数 free 专门用来做动态内存的释放和回收的。

和栈区使用不同,栈区空间使用后是由操作系统自动回收的。例如一个函数return后,为这个函数在栈区开辟的空间就被操作系统自动回收了(函数栈帧销毁)。而堆区的申请的内存则是整个程序结束后才被操作系统回收,所以,当我们使用完堆区申请的内存后有,需要用 free 释放来将这块空间还给操作系统。

 free函数是用来释放动态开辟的内存的.

如果参数 memblock不是动态开辟的,那么free函数的行为是未定义的。

如果参数 memblock为NULL,那么free函数什么也不做。

请看下面的例子:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
	int i = 0;
	int* p = (int*)malloc(5 * sizeof(int));//开辟动态内存空间
	if (p != NULL)
	{
		//使用
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
	}
	//释放
	free(p);
	p = NULL;


	return 0;
}

使用完开辟的堆区空间后要进行释放,然后将指向这块空间的指针置为NULL。

置为NULL的原因:

free函数只是将这块空间还给操作系统,并不会把指向这块空间的指针置为NULL,这就意味着在free之后,该指针就是一个野指针,是不能进行访问的。所以在free之后要置为NULL。以免造成野指针的访问。

<2> calloc

C语言还提供了一个进行动态内存开辟的函数 calloc

 参数size是希望开辟的单位的大小,参数num是所需开辟的单位的个数,使用规则和malloc函数是一样的。

例子如下:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
	int i = 0;
	int* p = (int*)calloc(5, sizeof(int));//开辟动态内存空间
	if (p != NULL)
	{
		//使用
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
	}
	//释放
	free(p);
	p = NULL;


	return 0;
}

malloc 和 calloc 的区别:

malloc函数在开辟空间时,只是给了一块空间的访问权限,但并未对所申请区域的数据进行初始化,而calloc函数在开辟时,会将所申请区域的数据全部初始化为0。(未初始化时内存空间内存储的是随机值)

如果我们在申请动态内存空间时,需要对所申请的空间初始化,那么可以使用calloc函数。

<3> realloc

realloc函数的出现让动态内存管理更加灵活。

realloc函数可以对动态开辟的内存大小进行调整。

memblock参数是需要调整的动态内存空间的指针,size参数是所需调整到的字节数。

返回值是调整之后的空间的首地址。

注:realloc函数在调整空间时,会有两种情况:

        1、原有空间之后有足够大的空间

那么就会在原有空间后面直接追加空间,原来空间的数据不发生改变。返回的指针指向的空间仍然是realloc之前的空间,不过是扩容了。

        2、原有空间之后没有足够大的空间

扩展的方法是:在堆空间上另外找一块合适大小的连续空间来使用,原有的数据会被挪到新开辟的空间中,返回的指针指向的空间也是新的。原来的那块空间就会被回收。

如果扩容失败,会返回NULL 。

所以在接收realloc返回的指针时,要创建一个中间变量,用来判断是否开辟成功,再决定是否将新开辟的空间交给原来的指针保管。若是直接将返回值交给缘来的指针,若开辟失败,原指针就被置为NULL,原数据也就不能访问了

使用例子:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
	int i = 0;
	int* p = (int*)calloc(5, sizeof(int));//开辟动态内存空间
	if (p != NULL)
	{
		//使用
		for (i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 5; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
	}
	printf("\n");
	//调整
	int* ptr = (int*)realloc(p, sizeof(int) * 10);
	if (ptr != NULL)
	{
		//使用
		p = ptr;
		for (i = 0; i < 10; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", *(p + i));
		}
	}
	else
	{
		printf("扩容失败\n");
	}
	//释放
	free(p);
	p = NULL;


	return 0;
}

其中ptr就是一个中间变量。

特殊情况:

若给relloc传入空指针,即realloc(NULL, size);此时功能类似malloc 。

水平有限,欢迎指正。(下一篇就写写使用这些函数容易出错的点吧)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值