C++语言内存管理是指:对系统的分配、创建、使用这一系列操作。在内存管理中,由于是操作系统内存,使用不当会造成很麻烦的后果。本文将从系统内存的分配、创建出发,并且结合例子来说明内存管理不当会造成的结果以及解决方案。
一:内存
在计算机中,每个应用程序之间的内存是相互独立的,通常情况下应用程序A并不能访问应用程序B,当然我们可以使用一种特殊技术,让他们可以互相访问,但这个不是本文的重点。比如在计算机中,一个视频播放程序与一个浏览器程序,他们的内存并不能访问,每个程序所拥有的内存是分区进行管理的。
在计算机系统中,运行程序A将会在内存中开启程序A的内存区域1,运行程序B将会在内存中开辟程序B的内存区域2,内存1和内存2之间逻辑分离。
1.1 内存四个区
在程序A开辟的内存区域1会被分为几个区域,这就是内存四区,内存四区分为栈区、堆区、数据区和代码区。
栈区:指的是存储一些临时变量的区域,临时变量包括了局部变量、返回值、参数、返回地址等,当这些变量超出了当前作用域时将会自动弹出。该栈的最大存储是有大小的且固定的,超过该代销将会造成栈溢出。
堆区:是一个比较大的内存空间,主要用于对动态内存的分配,在程序开发中一般是开发人员进行分配和释放,若在程序结束时都还未释放,系统将自动进行回收。
数据区:主要存放全局变量、常量、静态变量,数据区又可以划分为:全局区和静态区,全局变量和静态变量将会存放到该区域。
代码区:比较简单,主要存储可执行代码,该区域的属性是只读的。
1.2 通过代码证实内存四区的底层结构
栈区
由于栈区和堆区的底层结构比较直观的表现,我们在此使用代码只演示这两个概念来查看代码观察栈区的内存地址分配情况。
// main.cpp
#include<iostream>
int main() {
int a = 0;
int b = 0;
int c = 0;
printf("变量a的地址: %d", &a);
printf("\n变量b的地址: %d", &b);
printf("\n变量c的地址: %d", &c);
// 打印结果
}
为什么它们之间的地址不是增加而是减少呢?那是因为栈区的一种数据存储结构为先进后出,如图:
- 首先栈的顶部为地址的“最小”索引,随后往下依次增大,但是由于堆栈的特殊结果,我们将变量a先进行存储,那么它的一个索引地址将会是最大的,随后依次减少。(即 6422300)
- 第二次存储的变量时b,该值得地址索引比a小4个字节(因为int类型在32位系统中占4个字节),所以在a地址的基础上减少4个字节(即:6422296)
- 第三次存储变量c,该值得地址索引比b小4个字节(即:6422292)
- 第三次存储变量d,该值得地址索引比c小1个字节(即:6422291),因为char字符占一个字节
- 由于a、b、c、d这四个变量同属一一个栈内,所以他们地址的索引是连续性的,但是如果我们创建一个静态变量将会是怎样的了 ?
数据区
我们定义一个全局变量和 静态变量看看效果
#include<iostream>
int e = 0;
int main() {
static int f = 0;
printf("\n var e address: %d", &e);
printf("\nvar f address: %d", &f);
}
// 打印结果
var e address: 8430692
var f address: 8430696
从这个例子可以看出,数据区的地址不是栈结构,它是由低向高分配的,这里还是和栈区有点区别的。
以上例子其实也说明了栈区的特性,就是容量具有固定大小,超过最大容量将会造成栈溢出
#include<iostream>
int main() {
char char_arr[1024 * 1000000000];
char_arr[0] = 'a';
}
// 编译之后直接会报错
1.3 :为了解决上面这个问题(开辟大容量内存)
堆并没有栈一样的结构,也没有栈一样的先进后出。我们需要人为的对内存进行分配使用。
#include<iostream>
#include<string>
int main() {
//定义一个指针:指向一个大容量的内存地址
char* p1 = (char*)malloc(1024 * 1000);
// 把字符串"here is heap"赋值到指针 p1指向的空间中
std::string s = "here is heap";
strcpy_s(p1, 1024,s.c_str());
//打印内存空间的地址
printf("p1 storage address: %d", p1);
printf("\np1 storage: %s", p1);
}
//打印结果
p1 storage address: 19443776
p1 storage: here is heap
多说一点
char *p=new char[10];
p="hello";
delete p[];//此步释放时将报错❌这是因为:在C++中,字符串常量"hello"被保存在常量存储区,而p="hello"操作是改变了指针的指向,使得指针p指向了常量存储区的"hello",造成了初始在堆上开辟的内存泄露,而delete无法释放常量存储区的内存,导致出错。
二:malloc和free使用
在 C 语言(不是 C++)中,malloc 和 free 是系统提供的函数,成对使用,用于从堆中分配和释放内存。malloc 的全称是 memory allocation 译为“动态内存分配”。
2.1 malloc和free
在开辟堆空间时我们使用的函数为 malloc,malloc 在 C 语言中是用于申请内存空间,malloc 函数的原型如下:
void *malloc(size_t size);
在 malloc 函数中,size 是表示需要申请的内存空间大小,申请成功将会返回该内存空间的地址;申请失败则会返回 NULL,并且申请成功也不会自动进行初始化。
细心的同学可能会发现,该函数的返回值说明为 void *,在这里 void * 并不指代某一种特定的类型,而是说明该类型不确定,通过接收的指针变量从而进行类型的转换。在分配内存时需要注意,即时在程序关闭时系统会自动回收该手动申请的内存 ,但也要进行手动的释放,保证内存能够在不需要时返回至堆空间,使内存能够合理的分配使用。
未完待续:看完这篇你还能不懂C语言/C++内存管理?
把内存管理理解好,C语言真的不难学。今天带你“攻破”内存管理