动态内存分配存在的原因:
节省内存空间。若预先开辟一块空间,小了导致不够用数组越界,大了用不完造成内存空间浪费。
malloc函数、calloc函数与free函数
malloc
#include <stdlib.h>
void* malloc (size_t size)
//void*方便后续使用 需要什么类型的指针 强制类型转换一下即可
//size的大小是字节的大小
//例如要用malloc模拟int arr[10] = {0}
int* p = (int *) malloc(40);
calloc
#include <stdlib.h>
void* calloc (size_t num, size_t size)
//num表示元素个数
//size表示每个元素的大小
//例如要用calloc模拟int arr[10] = {0}
int* p = (int *) calloc(10,sizeof(int));
对malloc,若开辟成功,函数返回一个void*指针;若因为堆内存空间不足开辟失败,函数返回NULL。
malloc和calloc函数的区别一个是参数不一样,另一个是malloc不会初始化数组,而calloc会初始化数组元素为0。所以直接返回地址的malloc效率会更高一些。
realloc
#include <stdlib.h>
void* realloc(void* ptr, size_t size);
//ptr表示一个由malloc, calloc, realloc开辟的指针
//若ptr是空指针 那realloc和malloc的作用是相同的
realloc的作用是扩容,但是扩容会有几种不同的情况发生。
Ⅰ:在堆中,ptr后面有充足的空间,则顺利扩容,返回的指针值和ptr相同
Ⅱ:若ptr的直接后面没有足够的目标空间开辟,即后面的空间可能已经被别的数据占用,则函数会在另一个位置重新开辟完整的空间,返回的指针值就和ptr不同,是一个全新的地址,同时原来ptr代表的一块内存空间也被释放销毁
Ⅲ:若整个堆中都找不到足够的空间,函数会返回空指针,开辟失败
针对情况Ⅲ,使用realloc函数时候要注意:
①不直接用函数返回的指针赋值给原来的指针,因为要是开辟失败返回空指针,那原来的地址也找不到了
②对返回的指针进行判断,不是空指针再继续使用
int* ptr = (int*) malloc(100);
int* tmp = (int*) realloc(ptr, 1000);
if (NULL != tmp)
ptr = tmp;
free
内存开辟后,若使用完毕了最好还是能够回收内存,就需要用上free函数
#include <stdlib.h>
void free (void* p)
注意!malloc函数得到的指针最开始是存有一个堆中的地址的,free之后,原来堆中被占用的内存可以给别处用了,但是指针存的地址还在,这就导致形成了一个野指针,所以需要把指针赋一个NULL,这样他就无法再次被使用了。
有一个感觉没啥用的函数strerror(errno)
#include <stdlib.h>
#include <errno.h> //需要头文件
#include <string.h>
int main()
{
int* arr = (int*)malloc(40000000000000000);
if (NULL == arr)
{
printf("%s\n", strerror(errno));
return 0;
}
free (arr);
arr = NULL;
return 0;
}
//输出Not enough space
常见的动态内存问题
对空指针NULL解引用
对动态开辟的空间越界访问
对非动态内存进行free释放
使用free释放内存的一部分
没有对动态内存释放导致内存泄漏
函数返回栈区地址问题
探究这个问题之前我们还是先来复习一下内存的区域功能划分,如图:
临时的数据都是再栈上开辟的,而动态内存时在堆区开辟的。两者的区别在于栈区的数据会在局部函数结束后被销毁,比如说形参,局部函数中的变量,而堆区则会在程序执行结束后,或者主动释放后才销毁回收。这两者的区别就造成了返回栈区地址的问题。
常见错误的代码如下:
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
p确实存储了字符数组p的首元素地址,但是在GetMemory函数调用结束后,p[]所在的栈空间就被销毁回收了,字符数组的内容不复存在,返回值p代表的指针也成了空指针。
又如:
int* test(int* p)
{
int a = 10;
return &a;
}
int main()
{
int* i = 0;
i= test(&i);
printf("%d", *i);
//输出10
return 0;
}
这里同样时返回了栈区地址,为什么可以正确得到10的结果?
因为栈空间确实已经释放,但是原来10的空间暂时还没有被其他数据替换、占用,所以10的值并没有改变。假若在printf("%d", *i);前一行打印任何东西,就可以发现不再能输出结果10。