C语言 动态内存管理
二球悬铃木丶 人气:0一.为什么存在动态内存分配
我们已经掌握的内存开辟方式有:
int a = 10;//在栈空间开辟4个字节的连续空间 int b[20] = { 0 };//在栈空间开辟20个字节的连续空间
这种开辟空间的方式有以下特点:
1.开辟空间的大小是固定的
2.开辟数组时必须指定大小
初学数组时,我写过下面的错误代码。
int N; scanf("%d",&N); int a[N]={ 0 };
可N是变量,不能用于数组元素个数的初始化。
如果我们需要的空间大小在程序运行时才能知道,那就只能试试动态内存开辟了。
二.动态内存函数的介绍
1.malloc和free
void* malloc (size_t size);
void free (void* ptr);
malloc函数用于向内存申请一块连续可用的空间,并且返回指向这块空间的指针。
若开辟成功,返回指向这块空间的指针
若开辟失败,返回NULL指针,因此malloc的返回值一定要做检查
使用完malloc函数要用free释放申请的内存空间
#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)malloc(40);//开辟40个字节的栈空间 if (p == NULL) //检查是否为空指针 { perror("malloc"); return 1; } for (int i = 0; i < 10; i++) { *(p + i)=i; } free(p); //用完后释放空间,注意参数为首地址 p = NULL; //置为空指针 }
2.calloc
void* calloc (size_t num, size_t size)
calloc的两个参数分别为申请元素的个数和每个元素的大小,
使用和malloc差不多,但是申请的空间会被初始化为0,
#include<stdio.h> #include<stdlib.h> int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } for (int i = 0; i < 10; i++) { printf("%d ", *(p + i)); //输出为 10个0 } free(p); p = NULL; }
3.realloc
void* realloc (void* ptr, size_t size)
realloc用于重新设置要开辟内存空间的大小,可以是增大或减小
指针ptr是指向先前使用 malloc、calloc 或 realloc 分配的内存块的指针。
size 是新开辟内存空间的大小
若原空间后面未开辟的空间足够使用,则返回原先的起始地址
若原空间后面未开辟的空间不足以填满新开辟内存空间,
则会在某个地址开辟所需要的空间,free掉原空间的地址,
并且返回新的地址的起始地址
真 · 一条龙服务
若扩容失败,会返回空指针,因此也要检查是否是空指针
三.常见的动态内存错误
1.对NULL指针的解引用操作
void test() { int *p = (int*)malloc(INT_MAX/4); *p = 20; free(p); }
若p为空指针,则程序错误。
解决方案:检查是否为空指针
2.对动态开辟空间的越界访问
int main() { int* p = (int*)calloc(10, sizeof(int)); if (p == NULL) { perror("calloc"); return 1; } for (int i = 0; i <= 10; i++) //当i为10时,形成越界访问,程序出错 { printf("%d ", *(p + i)); } free(p); p = NULL; }
使用这块空间时要注意是否已经越界访问
3.对非动态开辟的空间使用free释放
int a = 10; int* p = &a; free(p);
一执行,程序崩溃了
4.使用free释放一块动态开辟空间的一部分
void test() { int* p = (int*)malloc(100); p++; free(p); }
同样会崩溃
5.对同一块开辟的空间多次释放
void test() { int* p = (int*)malloc(100); free(p); free(p); }
没错,又又又崩溃了,就不上图了
6.动态内存开辟忘记释放(内存泄漏)
如果使用空间后不释放,会导致内存泄漏。
内存泄漏的堆积,这会最终消耗尽系统所有的内存。
四.几个经典的笔试题
第一个
void GetMemory(char* p) //对空指针解引用 { p = (char*)malloc(100); //内存泄露 } void test() { char* str = NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main() { test(); }
p是str的一份临时拷贝,指向malloc申请的起始地址,
出了函数之后,内存还给系统,str仍为空指针,strcpy把“hello world”放进空指针
第二个
char *GetMemory(void) { char p[] = "hello world"; return p; } void test() { char* str = NULL; str=GetMemory(); //野指针str printf(str); } int main() { test(); }
定义字符串p,并返回p的地址
但是当出了这个函数,内存还给系统,没有使用权限
指针变为
第三个
void GetMemory(char** p,int num) //传址调用 { *p = (char*)malloc(num); } void test() { char* str = NULL; GetMemory(&str,100); strcpy(str, "hello world"); printf(str); //没有free } int main() { test(); }
打印hello world
没有释放空间
第四个
void GetMemory(char** p,int num) { *p = (char*)malloc(num); } void test() { char* str = (char*)malloc(100); strcpy(str, "hello world"); free(str); //还给你,我还要用,哼~ if (str != NULL) { strcpy(str, "!!!"); printf(str); } } int main() { test(); }
开辟100个字节的空间后,又把这块空间还给操作系统。
再次把“!!!”放进这块空间,非法修改
tips:动态内存管理是在堆区上进行的。
加载全部内容