动态内存分配基本使用及常见问题
为什么需要动态内存管理?创建一个数组,我们要为数组指定大小,int arr[10];
,这属于静态创建一个数组,数组arr存放在栈上。这样的创建方式有一些局限性,小了呢不够用,大了呢又浪费空间,因此要引入动态内存管理。
动态创建一个数组,不再受元素个数的限制,当元素个数与容量相等时,可以很方便地扩容。
如何动态内存管理,我们来介绍几个函数。
malloc
1 | void* malloc (size_t size); |
参数size为要为空间开辟的字节数,开辟成功后返回值为该空间的首地址,失败则返回NULL
.当size为0时,要看编译器如何处理,具体返回什么不确定。
动态开辟内存后,不需要再使用这块空间时,要使用free
函数释放内存。,否则会内存泄漏。free释放后这块内存可以再次被分配,但被释放的空间的值没有被改变,它仍然指向相同(无效)的位置。
free
专门用来释放动态分配的空间,如果空间为空,不执行任何操作。切记不可以用free来释放静态分配的内存空间。
1 |
|
calloc
1 | void* calloc (size_t num, size_t size); |
与malloc
相似,但是会在开辟后为空间初始化为0.参数num为要分配的元素数,size为每个元素的大小,总的内存空间为num*size个字节。
与malloc用法一致,不在举例。
realloc
1 | void* realloc (void* ptr, size_t size); |
该函数可以为动态空间扩容,参数ptr为要扩容的空间,size为扩容后的内存大小。
扩容成功返回该空间的首地址,失败返回空,所以为空间扩容时先创建一块临时变量指向该空间,为临时变量扩容,扩容成功再让要扩容的空间等于临时变量,以防扩容失败内容丢失。
扩容会遇到两种情况,1种是该块空间后没有额外的空间来扩容,这时会分配一块新空间,将旧空间的内容移到新空间,返回值自然也是新空间的首地址,2是该块空间空间充足,这就可以在该空间后连续扩容,无需再寻找新的空间。
1 |
|
输出结果为0~19,扩容成功。
问题
了解了动态内存分配函数的一些基础使用后,我们来看一些常见的问题。
代码1:这段代码有什么问题?
1 | void GetMemory(char *p) |
p是一个局部变量,是实参的一份临时拷贝,出了GetMemory函数会自动销毁。我们为p开辟了100个字节的空间,出了函数后p销毁,找不到这块空间的首地址,却没对该空间进行内存释放,会造成内存泄漏。
此外str仍是空指针,将“hello world”拷贝到一块空指针,会造成非法访问,程序崩溃。
我们可以试着修改,将传值调用改成传址调用,这样p和str指向了同一块空间,最后别忘了free释放内存。
1 | void GetMemory(char *p) |
代码2:
1 | char *GetMemory(void) |
p是一个局部变量,是实参的一份临时拷贝,return p
返回的是局部变量p的首地址,str收到了这个地址。不巧的是出了GetMemory函数p这块地址就还给了操作系统,所以str找不到这块地址,造成了野指针。