malloc
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的起始地址。
在堆上开辟空间
- 如果开辟成功,则返回一个指向开辟好空间的指针。
- 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
- 返回值的类型是
void*
,所以malloc
函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。
free
函数用来释放动态开辟的内存。
- 如果参数
ptr
指向的空间不是动态开辟的,那free函数的行为是未定义的。 - 如果参数
ptr
是NULL
指针,则函数什么事都不做。
int arr[10] = { 0 };//在栈上开辟40个字节
int* p = (int*)malloc(40);
//在内存上开辟好空间,把起始地址强制类型转换成int*后赋给p
free(p);//把p所指向的空间还给操作系统
p = NULL;
calloc
malloc和calloc都是用来内存分配的
malloc函数只负责在堆区申请空间,并且返回起始地址,不初始化内存空间
calloc函数在堆区上申请空间,并且初始化为0,返回起始地址
realloc
int main()
{
int* p = (int*)calloc(10, sizeof(int));//失败返回NULL
//开辟十个整形,每个元素大小是sizeof(int)
//空间不够,增加20个int
int* ptr=(int*)realloc(p, 20 * sizeof(int));
//使用临时变量ptr,防止realloc内存开辟失败了返回空指针,使p改变
if (ptr != NULL)
p = ptr;
free(p);
p = NULL;
return 0;
}
常见内存开辟的错误
对空指针进行解引用
int main()
{
int* p = (int*)malloc(20);
/*if (p == NULL)//检查
return -1;*/
*p = 0;//如果内存开辟失败,返回NULL
//空指针解引用就有问题
return 0;
}
对动态开辟空间的越界访问
int main()
{
int* p = (int*)malloc(200);
//开辟200个字节,最多容纳50个整形
if (p == NULL)//检查
return -1;
for (int i = 0; i < 80; i++)//越界访问
*(p + i) = i;
free(p);
p = NULL;
return 0;
}
对非动态开辟内存使用free
释放
int main()
{
int a = 10;
int* p = &a;//p是栈上开辟空间
free(p);//错
p = NULL;
return 0;
}
使用free
释放一块动态开辟内存的一部分
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)//检查
return -1;
for (int i = 0; i < 10; i++)
*p++ = i;
free(p);//错,p已经改变
p = NULL;
return 0;
}
对同一块动态内存多次释放
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)//检查
return -1;
free(p);
free(p);//错
p = NULL;
return 0;
}
动态开辟内存忘记释放(内存泄漏)
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)//检查
return -1;
//在堆区上申请的空间,有两种回收的方式
// 1.主动free 2.程序退出时,申请的空间也会回收
//忘记释放
getchar();
return 0;
}
笔试题
1.下面程序运行结果是?
void GetMemory(char* p)
{
p = (char*)malloc(100);//p的改变不会影响str
}
void Test(void)
{
char* str = NULL;
GetMemory(str);//str是形参
strcpy(str, "hello world");//程序崩溃
printf(str);
}
int main()
{
Test();
return 0;
}
程序运行会崩溃, 1.str传给p时是值传递,p是str的临时拷贝,当malloc开辟的空间起始地址放在p中的时候,不会影响str,str依然为NULL;
2.当str是NULL,strcpy想把hello world拷贝到str指向的空间时,程序就崩溃了,因为NULL指针指向的空间是不能直接访问的
3.存在内存泄漏,malloc开辟的空间,从来没有被主动释放(free)
如何修改?
方法一:
void GetMemory(char** p)
{
*p = (char*)malloc(100);
}
void Test(void)
{
char* str = NULL;
GetMemory(&str);//传址
strcpy(str, "hello world");
printf(str);
free(str);//释放
str = NULL;
}
方法二:
char* GetMemory()
{
char* p = (char*)malloc(100);
return p;//返回值的方式带回malloc出来的空间的起始地址
}
void Test(void)
{
char* str = NULL;
str=GetMemory();
strcpy(str, "hello world");
printf(str);
free(str);//释放
str = NULL;
}
2.下面程序运行结果是?
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char* str = NULL;
str = GetMemory();
printf(str);
}
程序运行打印随机值:
为什么?返回栈空间地址的问题,p[]是局部变量,出了函数作用域空间会还给操作系统
更简单的理解
int* test()
{
int n = 10;
return &n;
}//n的空间已经还给操作系统
int main()
{
int* p=test();
printf("%d ", *p);//错
return 0;
}
3.下面程序错误的原因?
free之后要记得置空
void Test(void)
{
char* str = (char*)malloc(100);
strcpy(str, "hello");
free(str);
if (str != NULL)//str到这是野指针
{
strcpy(str, "world");//非法访问内存
printf(str);
}
}
int main()
{
Test();
return 0;
}