动态内存分配浅论

动态内存分配在初级C语言中是一个极其重要的章节,不管是现在还是将来,学好动态内存分配的我们将会终生受益。本篇文章较适合于初学者阅读,在初期学到的内容基本上都会在这里呈现。本篇博客从写入到结束历史5小时,觉得这篇文章总结的较为详细的话别忘了收藏与关注哦(我愿称之为舔狗级别讲解),后续我将更新更深一层的动态内存分配。

在学到这知识点之前的我们是否会这样的问题,在之前我们学的一般的变量都要先确定大小,但在使用的时候往往不太如人意,就拿排序来说吧,如果我们所要排序的数字小于编译前确定的大小,是否会造成内存浪费?若大于编译前确定的大小,那我们在编译前分配的内存就显得不够用了。那有没有什么方法能够解决这个问题呢?答案当然是运用动态内存分配

目录

内存的四块区域

malloc()和free()

calloc()

malloc()和calloc()的区别

realloc()

动态内存分配那点坑


内存的四块区域

在进入正题之前我想先描述一下内存的四块存储区域:栈,堆,静态(存储)区,代码区。

栈区->局部变量及函数的形式参数;

堆区->动态内存分配;

静态区->全局变量及静态变量;


#include <stdio.h>
int b = 0;//全局变量
int main()
{
	int a = 0;//局部变量
}

比如说int a 是局部变量,在栈区,在前面加上static以后则变成静态变量,在静态区。

栈区 部变量及函数的形式参数
堆区 动态内存分配
静态区

全局变量及静态变量

static int a=0

malloc()和free()

malloc的原型     void* malloc(size_t size)                        free的原型     void free(void * memblock)                                                              

动态内存是如何开辟与释放的呢?malloc函数可以在堆上申开辟一个我们想要的大小的空间;free函数能在当动态申请的空间不再使用的时候释放还给操作系统。注意:malloc()和free()是成对存在的,此点在下文会描述到。


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));//该函数返回一个指向错误字符串的指针,该错误字符串描述了错误 errno
	}
	else
	{
		int i = 0;
		for (i = 0; i < 10; i++)
		{
			*(p + i) = i;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", *(p+i));
		}
	}
	free(p);  //将不再使用的空间返还给操作系统,若缺少此条件则会造成内存泄漏。
	p = NULL;
	return 0;
}

如上我们想内存申请了40个字节大小的空间并返回指向这块空间的指针。在使用malloc函数后一定要记得检验malloc的返回值,如果开辟成功,则返回一个指向开辟好空间的指针;如果开辟失败,则返回一个NULL指针。返回值的类型是void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。像上面这个例子则是将开辟空间的类型设为int*

calloc()

calloc函数原型    void* calloc(size_t num,size_t size)   

 其中第一个参数size_t num是想要开辟的元素个数,第二个参数size_t size是元素的大小

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* pc = (int*)calloc(10, sizeof(int));       //calloc()与malloc()的使用并没有什么不同,不同之处在于------>下面给你揭晓~
	if (pc == NULL)
	{
		printf("%s\n", strerror(errno));
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(pc + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *(pc + i));
	}
	free(pc);
	pc = NULL;
	return 0;
}

malloc()和calloc()的区别

malloc函数生成的是xx个字节大小未被初始化的内存空间,而calloc函数生成的是xx个字节已被初始化为0的空间。话不多说,上图上代码!

malloc()如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* pc;
	pc = (int*)malloc(sizeof(int*)*10);
	printf("%p\n", pc);
}

 calloc()如下:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* pc;
	pc = (int*)calloc(10,sizeof(int));
	printf("%p\n", pc);
}

仔细观察内存地址会发现,malloc()与calloc()开辟的内存空间有 有无初始化 之别。可以自己去编译器那里写段代码按f10,打开监视与内存自己去调试一下你就会深信不疑啦,希望看到这的你能自己去调试一下,眼动不如手动,手动如下,上图:

 

realloc()

 realloc函数模型      void* realloc(void* ptr,size_t size)

其中第一个参数是指向某个动态内存的指针,第二个参数是想要加入的空间的大小

话不多说,上代码:

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

其中,INT_MAX含义如→

读者可以自己写代码去实现一下,若没有realloc()是无法实现0 1 2 3 4 5 6 7 8 9的,这就是追加空间的妙用。但realloc()追加空间的道路并不是一帆风顺的。

realloc()使用注意事项:

如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一个新的内存区域开辟一块满足需求的空间,并且把原来内存中的数据拷贝回来,释放旧的内存空间
得用一个新的变量来接受realloc函数的返回值

 以上就是鄙人对malloc()、free()、calloc()、realloc()的浅见。

接下来我要列举一些小白经常会犯的错误,希望看到这篇文章的你能避开这些坑。

动态内存分配那点坑

1

1.对NULL进行解引用操作;
2.对动态开辟内存的越界访问;
3.对非动态开辟内存使用free释放;
4.使用free释放动态开辟内存的一部分;
5.对同一块动态内存的多次释放;
6动态开辟内存忘记释放(内存泄漏)。

接下来大部分代码段是为了讲解那些坑,并不能实现!!!

1.对NULL进行解引用操作:当要开辟的空间非常非常大的时候就可能会出现这个情况

int*p=(int*)malloc(95845635894)  //太大啦所以error

 2.对动态开辟内存的越界访问:用malloc()开辟了xx个字节大小的空间,妄想把xx+N*(N*为大于0的数)大小的东西塞到里面去

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

3.对非动态开辟内存使用free释放:简单来说就是栈区的不归堆区管

{
    int a = 10;
	int* pc = &a;
	free(pc);
	pc = NULL;
}

error!

4.使用free释放动态开辟内存的一部分:简单来说就是,月下的你不复当年模样(循环的时候发现她已经不再是原来的那个她,因为她已经不在原地了),上代码:

int main()
{
	int* p = (int*)malloc(40);  
	if (p == NULL)
	{
         printf("%s\n", strerror(errno));
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*p++ = i;    //这里是问题所在
	}
	return 0;
}

5.对同一块动态内存的多次释放:多次运用free()

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		
	}
	free(p);     //1个
	p = NULL;
	

	free(p);      //2个
}

6.动态开辟内存忘记释放(内存泄漏):末尾没有free()你的电脑就很不free了,要意识到这是个很严重的问题!!!

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		
	}
	else
	{
		int i = 0;
		for (i = 0; i <5; i++)
		{
			*(p + i) = i;
		}
	}
	return 0;
}

不free掉,后果很严重!

感谢能阅读到这的你,如果觉得这篇文章对你理解动态内存分配有帮助的话,请留下你的收藏与点赞哦。

Guess you like

Origin blog.csdn.net/qq_64263760/article/details/121883558