一、C/C++程序的内存开辟
1.栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
2.堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。
3.数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。
二、柔性数组
也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。c99中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。
#include<stdio.h> struct S { int n; char c; int arr[];//int arr[0]//柔性数组成员 }; int main() { return 0; }
三、柔性数组的特点
- 结构中的柔性数组成员前面必须至少一个其他成员。
以上代码为例下面这种代码不满足前面至少有一个其他成员是错误的
#include<stdio.h> struct S { int arr[];//int arr[0]//柔性数组成员 }; int main() { return 0; }
- sizeof返回的这种结构大小不包括柔性数组的内存。
由下面代码可以看到我们打印出的结果是8我们之前学过结构体内存对齐n从0开始占4个字节,c一个字节总共是5个字节,显然不是4的倍数所以应该是8,这里面并没有包含上柔性数组的内存
- 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
例如下列代码我们假设数组arr中存放10个元素,我们就要提供8+40=48个字节;8 —>int n和char c所占内存我们上面算过是8;40 —>存放10个int元素的数组大小
(1).柔性数组方案
第一个好处是:方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
#include<stdio.h> #include<errno.h> #include<stdlib.h> struct S { int n; char c; int arr[];//int arr[0]//柔性数组成员 }; int main() { struct S* pc = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int)); if (pc == NULL) { printf("%s\n", strerror(errno)); return 1; } pc->n = 10; pc->c = 'w'; int i = 0; for (i = 0; i < 10; i++) { pc->arr[i] = i; } for (i = 0; i < 10; i++) { printf("%d ", pc->arr[i]); } free(pc); pc = NULL; return 0; }
当然我们也可以通过扩容来改变数组中的大小,比如我们从存放10个元素改为存放20个元素,我们realloc来调整
#include<stdio.h> #include<errno.h> #include<stdlib.h> struct S { int n; char c; int arr[];//int arr[0]//柔性数组成员 }; int main() { struct S* pc = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int)); if (pc == NULL) { printf("%s\n", strerror(errno)); return 1; } pc->n = 10; pc->c = 'w'; int i = 0; for (i = 0; i < 10; i++) { pc->arr[i] = i; } for (i = 0; i < 10; i++) { printf("%d ", pc->arr[i]); } //调整arr数组大小 struct S* ptr = (struct S*)realloc(pc, sizeof(struct S) + 20 * sizeof(int)); if (ptr == NULL) { printf("%s\n", strerror(errno)); return 1; } else { pc = ptr; } free(pc); pc = NULL; return 0; }
(2).结构体中指针方案
第二个好处是:这样有利于访问速度.
连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正你跑不了要用做偏移量的加法来寻址)
#include<stdio.h> #include<errno.h> #include<stdlib.h> struct S { int n; char c; int* arr; }; int main() { struct S* ps = (struct S*)malloc(sizeof(struct S)); if(ps==NULL) { perror("malloc"); return 1; } int* ptr = (int*)malloc(10 * sizeof(int)); if (ptr == NULL) { perror("malloc1"); return 1; } else { ps->arr = ptr; } ps->n = 10; ps->c = 'w'; int i = 0; for (i = 0; i < 10; i++) { ps->arr[i] = i; } for (i = 0; i < 10; i++) { printf("%d ", ps->arr[i]); } //调整arr数组大小 ptr = realloc(ps->arr, 20 * sizeof(int)); if (ptr == NULL) { perror("realloc"); return 1; } else { ps->arr = ptr; } free(ps->arr); ps->arr = NULL; free(ps); ps = NULL; return 0; }
😽总结
😽Ending,今天的动态内存管理(下)内容就到此结束啦~,如果后续想了解更多,就请关注我吧。