C语言编程C++动态内存分配示例讲解
目录
- 动态内存管理
- 为什么存在动态内存分配
- 动态内存函数的介绍
- malloc申请空间和free释放空间
- 有借有还 free释放内存
- calloc申请内存
- realloc调整动态内存的大小
- realloc使用的注意事项
- 当然realloc也可以直接开辟空间
- 常见的动态内存错误
- 1.对NULL指针的解引用操作
- 2.对动态开辟空间的越界访问
- 3.对非动态开辟内存使用free释放
- 4.使用free释放一块动态内存开辟的一部分
- 5.对同一块动态内存多次释放
- 6.动态开辟内存忘记释放(内存泄漏)
- 几个面试题
- 题目1
- 题目2
- 题目3
- 题目4
- C/C++程序的内存开辟
- C/C++程序内存分配的几个区域:
动态内存管理
为什么存在动态内存分配
我们到现在为止掌握的是什么样的内存开辟方式呢
//创建一个变量int val = 20; //局部变量在栈空间中开辟4个字节int g_val = 10; //全局变量在静态区中开辟4个字节//创建一个数组char arr[10] = {0}; //局部区域 在栈空间中开辟10个字节连续的空间char g_arr[5] = {0}; //全局区域 在静态区空间中开辟5个字节的连续空间
文章图片
但是上述的开辟空间的方式有两个特点:
1.空间开辟大小是固定的。
2.数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。 这时候就只能试试动态内存开辟了。
c99是支持变长数组的,但现在很多编译器就不支持c99,连vs都不支持,所以就有动态内存的概念
动态内存函数的介绍
malloc申请空间和free释放空间
c语言提供了一个动态内存开辟的函数
void* malloc(size_t size);
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。
1.如果开辟成功,则返回一个指向开辟好空间的指针。
文章图片
2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
文章图片
3.返回值的类型是 void ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。*
文章图片
4.如果参数 size 为0,malloc的行为是标准未定义的,取决于编译器。
#include#include#include#includeint main(){ //向内存申请10个整形的空间 int* p = (int*)malloc(10 * sizeof(int)); if (p == NULL) {//把开辟失败的信息打印出来printf("%s",strerror(errno)); } else {//正常使用空间int i = 0; for (i = 0; i < 10; i++){*(p + i) = i; //在找下标为i的元素}for (i = 0; i < 10; i++)//再把每个元素打印出来{printf("%d ", *(p + i)); } } return 0; }
文章图片
那我们可不可以看开辟失败的呢
我们可以用INT_MAX(他是整形最大),一个超级大的数字
文章图片
文章图片
文章图片
有借有还 free释放内存
free函数用来释放动态开辟的内存。
1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
2.如果参数 ptr 是NULL指针,则函数什么事都不做。
文章图片
文章图片
注意
malloc和free是成对使用的,谁开辟谁释放
calloc申请内存 在内存中开辟一个数组,把元素都改成零
函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
文章图片
与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0
文章图片
realloc调整动态内存的大小 当然我们可以申请空间,但会不会遇到申请的空间不够了,想要增加一些些,大了想要去掉一些些
文章图片
realloc使用的注意事项
1.如果p指向的空间之后有足够的内存空间可以追加,则直接追加,后返回p
2.如果p指向的空间之后没有足够的内存空间可以追加,则realloc函数会重新找一块新的内存区域,开辟一块满足需求的空间,并且把原来的内存中的数据拷贝回来,释放旧的内存空间,最后返回新开辟的内存空间地址
3.但也有一个大问题,就是开辟INT_MAX,用新的变量ptr来接收realloc返回值
文章图片
文章图片
当然realloc也可以直接开辟空间
文章图片
常见的动态内存错误
1.对NULL指针的解引用操作
#include#includeint main(){ int* p = (int*)malloc(40); //没成功就会有大问题 int i = 0; for (i = 0; i < 10; i++) {*(p + i) = i; } free(p); p = NULL; return 0; }
文章图片
所以为了防止没有开辟动态内存成功就需要做个判断
文章图片
2.对动态开辟空间的越界访问
#include#include#include#includeint main(){ int* p = (int*)malloc(5*sizeof(int)); if (p == NULL)//这里我的确判断有没有开辟成功了 {printf("%s", strerror(errno)); } else {int i = 0; for (i = 0; i < 10; i++)//但是我这里访问10个整型的空间{*(p + i) = i; } } free(p); p = NULL; return 0; }
文章图片
3.对非动态开辟内存使用free释放
int main(){ int a = 0; int* p = &a; *p = 20; free(p); p = NULL; return 0; }
文章图片
4.使用free释放一块动态内存开辟的一部分
#include#include#include#includeint main(){ int* p = (int*)malloc(40); if (p == NULL) {return 0; //如果是空指针就直接返回,不干了 } int i = 0; for (i = 0; i < 10; i++) {*p++ = i; //这个++就是bug的地方 } //回收空间 free(p); p = NULL; return 0; }
文章图片
只要p不是指向申请的空间的首地址,其他地方都是错的
5.对同一块动态内存多次释放
#include#include#include#includeint main(){ int* p = (int*)malloc(40); if (p == NULL) {return 0; } //使用 //释放 free(p); //... free(p); return 0; }
文章图片
6.动态开辟内存忘记释放(内存泄漏)
#include#include#include#includeint main(){ while (1) {malloc(100); } return 0; }
文章图片
几个面试题
题目1
void GetMemory(char* p){ p = (char*)malloc(100); }void Test(void){ char* str = NULL; GetMemory(str); strcpy(str,"hello world"); printf(str); //这个写法和printf("%s",str); 是一样的}int main(){ Test(); return 0; }
问运行Test函数会有什么样的结果
文章图片
修改正确
#include#include#include#includevoid GetMemory(char* *p){ *p = (char*)malloc(100); }void Test(void){ char* str = NULL; GetMemory(&str); strcpy(str,"hello world"); printf(str); //这个写法和printf("%s",str); 是一样的 free(str); //用完就释放 str = NULL; }int main(){ Test(); return 0; }
文章图片
题目2
char* GetMemory(void){ char p[] = "hello world"; return p; }void Test(void){ char* str = NULL; str = GetMemory(); printf(str); }int main(){ Test(); return 0; }
请问运行Test 函数会有什么样的结果
输出随机值
文章图片
文章图片
修改正确
既然是p被销毁了,那我们让他不销毁就可以了延长它的生命周期用static
char* GetMemory(void){ static char p[] = "hello world"; return p; }void Test(void){ char* str = NULL; str = GetMemory(); printf(str); }int main(){ Test(); return 0; }
文章图片
题目3
void GetMemory(char **p, int num){*p = (char *)malloc(num); }void Test(void){char *str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); }
这题基本和第一题一样,不过这题就只有内存泄漏的错误
文章图片
修改正确
#include#includevoid GetMemory(char** p, int num){ *p = (char*)malloc(num); }void Test(void){ char* str = NULL; GetMemory(&str, 100); strcpy(str, "hello"); printf(str); free(str); //用完就释放,防止内存泄漏 str = NULL; }int main(){ Test(); return 0; }
文章图片
题目4
void Test(void){ char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); if (str != NULL) {strcpy(str, "world"); printf(str); }}
问题非常大的打印出结果
文章图片
文章图片
【C语言编程C++动态内存分配示例讲解】修改正确
#include#include#includevoid Test(void){ char* str = (char*)malloc(100); strcpy(str, "hello"); free(str); //这里考查的是free释放后并没有使str为NULL,所以下面if判断就没有作用,如果使他有作用就让str为NULL str = NULL; if (str != NULL) {strcpy(str, "world"); printf(str); }}int main(){ Test(); return 0; }
这道题真正目的就是让你什么都不打印
文章图片
C/C++程序的内存开辟
文章图片
C/C++程序内存分配的几个区域: 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。
数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
代码段:存放函数体(类成员函数和全局函数)的二进制代码。
以上就是C语言编程C++动态内存分配示例讲解的详细内容,更多关于C++动态内存分配的资料请关注脚本之家其它相关文章!
推荐阅读
- opencv|opencv C++模板匹配的简单实现
- C语言学习|第十一届蓝桥杯省赛 大学B组 C/C++ 第一场
- 动态组件与v-once指令
- c++基础概念笔记
- iview|iview upload 动态改变上传参数
- react-navigation|react-navigation 动态修改 tabBar 样式
- K14|K14 9/15销售提问法D2
- 牛逼!C++开发的穿越丛林真人游戏,游戏未上线就有百万人气
- 动态|诗歌《在精神科诊室》发《小说与诗》(香港)报
- C++Primer之|C++Primer之 函数探幽