赞
踩
目录
前言
动态存储分配适用于所有类型的数据,但主要用于字符串、数组和结构。
数组局部变量栈在windows中的默认大小为1M,Linux中默认大小为10M,因此在堆heap中进行内存申请——动态内存的开辟。
- malloc:分配内存块,但不对内存块进行初始化 (malloc最常用)
- calloc:分配内存块,并对内存块进行清零
- realloc:调整先前分配的内存块大小
为申请内存块调用内存分配函数时,由于函数无法知道计划存储在内存块中的数据类型,所以它的返回值类型不能是int,char等普通类型的指针,它的返回值是void*空指针。
需要用到动态内存的情况:
- 需要通过变量定义数组长度
- 需要定义大容量的数组
- 头文件:
#include<stdlib.h>
- 函数原型:
void * malloc(size_t size);
- 分配size_t个内存块,并且返回指向该内存块的指针。size_t:无符号整数类型。
引入头文件->函数内进行开辟内存->判断是否开辟成功->使用完之后释放内存->p=NULL(防止野指针出现)
- eg: 动态开辟10个(键盘获取)int 类型的一维数组
int num=10; int *p =(int*)malloc(sizeof(int)*num);//开辟了40个字节大小,再强转成int* if (p != NULL) printf("开辟成功"); //申请成功返回的是成功后的内存地址 else printf("开辟失败");
Windows:32位x86 1.7G~1.9G。linux:32位操作系统,3G
- 需要通过程序员进行内存释放——free(P);
- 需要一一对应(1个malloc对应1个free),否则会有内存泄漏
解决的问题:如果编写函数需要把两个字符串连接起来而不改变其中任何一个字符串,这就需要重新开辟一个新空间的新字符串通过strcat函数接收,此时对新字符串开辟空间就是用到malloc函数
例:
char * reault=(char*)malloc(strlen(str1)+strlen(str2)+1); //+1是因为strlen不计算'\0',需要为'\0'开辟空间
strcpy(result,str1);
strcat(result,str2);
注意:当使用malloc函数为字符串分配内存空间时,不要忘记包含空字符的空间。
其实字符串就是数组,动态分配数组会获得和字符串一样的遍历。
例如,要使用n个整数构成的数组,计算数组时所需要的空间数量要由sizeof运算符计算。
int * a=(int*) malloc(n*sizeof(int));
一旦a指向动态分配的内存块,就可以忽略a是指针的事实,可以把它用作数组的名字(数组和指针是紧密相连的),如下代码,可以使用下列循环对a指向的数组进行初始化:
- for(i=0;i<n;i++)
- a[i]=0;
头文件:
#include<stdlib.h>
函数原型:
- void *calloc(size_t num,size_t size);//size_t=unsigned int
- //num:申请单元个数;size:每个单元占用的字节数。
calloc函数为num个元素的数组分配内存空间,其中每个元素的长度都是size个字节。
如果要求的空间无效,那么此函数返回空指针。在分配了内存之后,calloc会通过把所有位设置为0的方式进行初始化。如下代码,calloc函数调用为n个整数的数组分配存储空间:
a=calloc(n,sizeof(int));
因为calloc函数会清楚分配的内存,而malloc函数不会,所以可能有时需要使用calloc函数为不同数组的对象分配空间。通过调用以1作为第一个实际参数的calloc函数,可以为任何类型的数据项分配空间:
- struct point (int x,y) *p;
- p=calloc(1,sizeof(struct point));
执行此语句之后,p将指向一个结构,且此结构的成员x和y都会被设置为0
一旦数组分配完内存,可能会发现数组过大或者过小。
realloc函数可以调整数组的大小使它更适合需要。
头文件:
#include<stdlib.h>
函数原型:
void *realloc(void *ptr,size_t size);
当调用realloc函数时,ptr必须指向先前通过malloc、calloc或realloc的调用获得的内存块,新尺寸可能会大于或者小于原有尺寸。虽然realloc函数不要求pte指向正在用作数组的内存,但实际上通常是这样的。
三种内容大小的扩充方式
1.后面的区域比较充裕,直接在原内存后面续上,如图所示。
2.前面有空余:将内存中的内容赋值到前面,后面空闲出来的内存相当于对其扩充内存,如图所示。
3.堆内存不足,扩展空间失败,realloc函数返回NULL。
引入头文件->malloc函数进行开辟内存->判断是否开辟成功->for将原来内容拷贝到新内存中->使用完之后free释放内存
eg:
#include<stdio.h> #include <string.h> #include<stdlib.h> #include<assert.h> int main() { int n = 1; int* p = (int*)malloc(sizeof(int) * n); if (p == NULL) { printf("内存申请失败"); exit(1);//0是正常退出,-或+异常退出。 } //假设一番操作后发现内存不足,要进行2倍扩容 p = (int*)realloc(p, sizeof(int) * n * 2); assert(p != NULL); return 0; }
- 当扩展内存块时,realloc函数不会对添加进内存块的字节进行初始化。
- 如果realloc函数不能按照要求扩大内存块,那么他会返回空指针,并且在原有的内存块中的数据不会发生改变。
- 如果realloc函数被调用时以空指针作为第一个实际参数,那么此时realloc=malloc
- 如果realloc函数被调用时以0作为第二个实际参数,它还是会释放掉内存块
一旦realloc函数返回,一定要对指向内存块的所有指针进行更新,因为realloc函数可能会使内存块移动到了其它地方。
malloc函数和其他内存分配函数都是从堆heap中开辟的内存。过于频繁调用这些函数可能会耗尽堆,这会导致函数返回空指针。
因此,引入free函数来释放不需要的内存,防止不可再被访问到的内存块存在内存泄漏情况。
头文件:#include<stdlib.h>
使用:
- p=malloc(...);
- q=malloc(...);
- free(p);
- p=q;
注意:free函数的实际参数必须是先前由内存分配函数返回的指针。参数如果是空指针,free调用就不起作用。参数如果是指向其他对象(变量或者数组元素)的指针,可能会导致未定义的行为。
调用free(p)函数会释放p指向的内存块,但是不会改变p本身,如果忘记了p不再指向有效内存块,修改p指向的内存,是严重的错误行为——悬空指针。
释放动态内存存在的问题
- 动态申请的内存首地址发生改变。如p++,而free(p)是释放p当前指向的地址,p当前指向的是无效内存,相当于野指针,free(p)系统会崩溃。
野指针的成因
指针未初始化
指针越界访问
指针释放后未置空
总言之都是指针指向非法地址
规避野指针
1. 初始化指针
2.开辟的指针释放后置为NULL;- 如果只定义了一次就不能多次释放一块内存空间,第二次释放就相当于free(NULL),没意义
- malloc申请内存,申请失败,在使用中数组下标会越界,此刻无法释放内存。
什么时候会释放内存
- 关机了之后
- 进程终止(可简单理解为正在进行的程序)
- 服务器发生内存泄露(可用vld进行内存泄漏测试)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。