动态内存的申请与应用

 c语言的内存模型,分为五大块,栈区,堆区,静态区,常量区,文件区

栈区(.stack)区,主要用于存放局部变量,堆区,也叫(.heap)区,是程序员自己用来申请动态内存的地方。数据区,也叫静态存储区,用来存放全局变量与字符串常量。文件区,文件区可以将写的代码翻译成机器可以识别的机器指令。

1.动态内存与静态内存的区别:

静态内存,是在栈区申请的,系统自己会对它进行开辟回收,比如,在主函数中定义一个数组,int arr[] = {1,2,3,4,5};系统自己会开辟20个字节的内存空间,函数结束后,系统自己会对这20个字节的内存进行回收。

动态内存,得自己申请,得与free函数搭配使用,free函数用来释放申请的内存,并且释放不需要等函数结束,而且随时开辟,随时释放。

c语言允许建立内存的静态分配,建立内存的静态分配就是用三个函数申请动态内存,三个函数分别是malloc,calloc,realloc.

malloc 进行动态内存的开辟,malloc的用法,void*malloc(unsigned int size); 返回值为void*,这个指针用于保存申请的那块动态内存的首地址,申请的动态内存没有规定用途,不知道这块内存用于存放什么类型的值,如果存放整型,那么返回值就是int*,后面括号内的内容是申请的内存大小,例如 int *malloc(sizeof(int)*10);申请了一块40个字节的动态内存,用于存放int 类型变量;

malloc的用法:1.int*p = (int*)malloc(sizeof(int)*10); 用p指针保存,申请的这块内存的首地址,类型强转的作用是看用户申请的这块动态内存用来存放什么类型的数据,malloc所在的头文件是<stdlib.h>或者<malloc.h>,进行动态内存申请后需要判断内存有没有申请成功,如何判断内存有没有申请成功,

内存申请是否申请成功很有必要,因为只有成功了才可以使用,堆区windows系统只有大约1.6到1.9个G,动态内存有时候会申请成功,有时候会申请失败,因为堆区也有别的软件使用动态内存,上述代码内存申请成功并使用结束后需要用free函数手动释放这块内存。p =NULL的作用,申请的内存被释放后指针p就指向了一个随机的地址,也叫无效地址,可能没有访问权限,p=NULL的作用就是将野指针p用NULL来赋值,防止后面不小心再次释放指针p,如果不赋值NULL,后面再次释放野指针,程序会崩

 free(NULL)可以正常运行,free野指针会崩,p==NULL的作用是防止二次释放引起的异常退出。

2.calloc的用法,申请一块动态内存并初始化。malloc函数申请的动态内存并不会初始化,如下图。使用格式与malloc也比较相似,void*calloc(size_t num(元素个数),size_t size(每个元素所占字节大小))

 

calloc用法与malloc用法大致相似,申请后判定内存是否申请成功,用完之后释放内存,并赋值空。

3.realloc,与前两个函数不同,它是用来对动态内存进行扩容的,realloc扩容有三种情况:

扩容时,有一个小点,Windows系统 堆区一般会有1.5到1.9G 的内存,有时候会更大,

 这两段代码分别验证了对p进行2倍扩容和100倍扩容后,p地址之间的变化。

realloc的用法:realloc是对原有p指向内存进行扩容,使用之前必须先进行内存的动态申请

int *p = (int*)malloc(sizeof(int)*10);   realloc 函数的返回值仍是void*,使用时进行类型转换,它的实参(p所指向的那块内存,字节数),例如,将p所指向的那块动态内存进行二倍扩容;

p = (int*)realloc(p,2*sizeof(int)*10);

关于内存泄漏:申请动态内存后,都得对所申请的内存通过free函数进行手动释放,如果申请了一块动态内存但不进行释放,就会造成内存泄漏。通常free函数与malloc,calloc,realloc函数联合使用。内存泄漏有下面几种情况,1.realloc函数对申请的动态内存进行扩容的第二种情况

 在堆区中用malloc函数申请一块一块40字节的动态内存,用realloc函数进行2倍扩容时后面没有足够的可分配的堆空间,会在内存的其它区域重新找一块动态内存,存放原来数据,p指向的地址也会发生变化,这时候,通过free函数释放p会发现,后面申请的80bite的动态内存会被释放,而前面malloc申请的40bite的动态内存并没有被释放,就会发生内存泄漏。内存泄漏的后果,如果动态申请的这块内存不能被释放,就会占据堆空间,无法被使用,久而久之,堆空间就无法使用了。2.同样是realloc扩容函数引起的内存泄漏,通过malloc或calloc函数申请的动态内存,扩容失败,原先有指针p指向申请的那块内存的首地址,扩容失败后,p会指向空,而原来那块内存也找不到了,也存在内存泄漏。

解决方法,同时也包含realloc的用法

int main() {
	int* p = (int*)malloc(sizeof(int) * 10);
	assert(p != NULL);
	int* q = (int*)realloc(p, 2 * sizeof(int) * 10);
	assert(q != NULL);
		free(p);
		p = q;//通过p操作扩容后的内存
		free(p);//p指针进行释放后就会成为一个野指针,用NULL赋值;
		p = NULL;
	
}

针对上述两种扩容失败引起内存泄漏的问题,所以扩容时,用一个新的指针变量q指向扩容后的新内容,如果扩容失败,释放原来的空间,即p所指向的内存空间,用完之后记得释放。至于q扩容失败,就不用考虑了。如果通过断言保证扩容成功,考虑p是否等于q,如果等于,说明是第一种扩容,后续可分配的内存空间足够大,释放时只需要free(p/q)其中一个即可,如果p!=q;我们先释放p所指向的那块内存,再将q的地址赋值给p,就又可以通过p来操作原有数据了。

int main() {
	int* p = (int*)malloc(sizeof(int) * 10);
	assert(p != NULL);
	int* q = (int*)realloc(p, 2 * sizeof(int) * 10);
	assert(q != NULL);
	if (p != q) {
		free(p);
		p = q;
		}
	else {
		free(p);
		p = NULL;
	}
	
}

3.用动态内存实现字符串的拷贝链接 

现在主函数中定义两个字符串数组,然后,申请一块动态内存并初始化,刚好够这两个字符串链接在一起,内存的大小由这两个字符串的大小决定。申请到动态内存后,用系统提供的strcpy函数先对第一个字符串数组进行拷贝,在封装的函数中进行链接.

封装一个my_strcat函数,字符串的拷贝链接,返回值为char*,形参为两个字符串,函数主体的内容是通过for循环,还有数组名,也是数组首元素的地址,也是指针,通过解引用,将第二个字符串数组赋值给到第一个字符串数组后面经初始化后的默认值,链接时注意,将第二个数组的结尾标记'\0'也一同赋值过去,代码如下

char* my_strcat( char* str, const char* src) {
	if (str == NULL || src == NULL) {
		return NULL;
	}
	int str_str = strlen(str);
	int str_src = strlen(src);
	for (int i = 0; i <= str_src; i++) {
		*(str+str_str + i) = *(src + i);
	}
	return str;
}
int main() {
	 char arr[] = "hello11231111";
	const char* brr = "world1456asdff";
	char* p = (char*)calloc((strlen(arr) + strlen(brr) + 1), sizeof(char));
	assert(arr != NULL);
	strcpy(p, arr);
	p = my_strcat(p, brr);
	printf("%s\n", p);
	free(p);
	p = NULL;
}

堆内存在什么情况下会被系统回收,1.free函数,2.关电脑,程序终止,所以说,一般的电脑内存泄漏一点无伤大雅,但是,服务器一般不能终止,这时候,动态内存的释放就很重要了。

栈内存会在函数调用结束后被系统回收。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值