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

C语言问题集——动态内存分配(1),第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 
#include 
#include 
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 
#include 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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 
#include 
#include 
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 。


水平有限,欢迎指正。


(下一篇就写写使用这些函数容易出错的点吧)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/568488.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存