文章目录
【 0. 内存管理 】
-
C++ 程序中的内存分为以下个部分:
- 栈区(stack) :函数内部 声明的所有变量 默认都占用栈内存 ,由 编译器自动分配与释放 。存放为运行时函数分配的局部变量、函数参数、返回数据、返回地址等。其操作类似于数据结构中的栈。如声明 int a;系统自动在栈空间中为a开辟空间。
- 堆区(heap) :程序中 未使用的内存 ,在程序运行时可用于 用户动态分配内存 。如C中的malloc以及C++中的new,如果程序员没有释放,程序结束时可能有OS回收。
- 全局区(静态区static) :存放全局变量、静态数据、常量。程序结束后由系统释放。全局区分为已初始化全局区(data)和未初始化全局区(bss)。
- 常量区(文字常量区):存放常量字符串,程序结束后由系统释放。
- 代码区:存放函数体(类成员函数和全局区)的二进制代码。
-
实例
p1和p2本身是在栈中的,但他们指向的地址是堆空间。
char*p1=(char*)malloc(10);
int*p2=new int(10);
【 1. new、delete 】
- 在 C++ 中,我们可以使用 new 运算符 为 给定类型(可以是包括数组在内的任意内置的数据类型,也可以是包括类或结构在内的用户自定义的任何数据类型)的变量在运行时分配堆内的内存,这会返回所分配的空间地址 。
- 如果我们不再需要动态分配的内存空间,可以使用 delete 运算符 删除之前由 new 运算符分配的内存 。
1.1 new 分配内存
- 基本语法
new data-type;
1.1.1 简单类型
- 基本语法
定义一个指向 指定类型(这里指向的是double)的指针,然后请求内存,该内存在执行时被分配:
double* pvalue = NULL; // 初始化为 null 的指针
pvalue = new double; // 为变量请求内存
/*或者*/
double* pvalue = new double; // 创建1个指针,可以指向double类型的变量。
- 实例
#include<iostream>
using namespace std;
int main()
{
double n=3.14;
double* pvalue= new double; //创建1个可以指向double类型的指针
pvalue=&n; // 将指针指向n
cout<<*pvalue;
return 0;
}
1.1.2 检查内存是否分配成功
- 如果自由存储区已被用完,可能无法成功分配内存,所以建议检查 new 运算符是否返回 NULL 指针,并采取以下适当的操作:
double* pvalue = NULL;
if( !(pvalue = new double ))
{
cout << "Error: out of memory." <<endl;
exit(1);
}
1.2 delete 释放内存
- 在任何时候,当我们觉得某个已经 动态分配内存的变量不再需要使用时,我们可以使用 delete 操作符释放它所占用的内存,基本语法:
delete pvalue; // 释放 pvalue 所指向的内存
- 实例
#include <iostream>
using namespace std;
int main ()
{
double* pvalue = NULL; // 创建1个指向 null 的double类型指针
pvalue = new double; // 为变量请求内存
*pvalue = 29494.99; // 在分配的地址存储值
cout << "Value of pvalue : " << *pvalue << endl;
delete pvalue; // 释放内存
return 0;
}
【 2. 数组的动态内存分配 】
2.1 一维数组
- 基本语法:
通过动态内存分配,申请一个长度为 n 的数组。
int *array=new int [n];//申请堆内存,将指针pvalue指向该内存
/* 上面的一行代码可替换为如下:
int* array = NULL; // 初始化为 null 的指针
array = new int[n]; // 申请堆内存,将指针array 指向该内存
*/
//释放内存
delete [] array;
- 实例
通过动态内存分配 生成一个元素数量为4的一维数组,令 a[i]=i,最后输出数组。
#include<iostream>
using namespace std;
int main()
{
int n=4;
int *a=new int[n]; //创建1个动态数组a
for(int i=0;i<n;++i)
a[i]=i;
for(int i=0;i<n;++i)
cout<<a[i]<<" ";
return 0;
}
2.2 二维数组
- 基本语法:
通过动态内存分配,创建一个二维的 mxn 数组。
// 动态分配内存
int **array;
array = new int *[m];
// 上面的两行代码可简化为:int **a=new int *[m];
for( int i=0; i<m; i++ )
{
array[i] = new int [n] ;
}
//释放内存
for( int i=0; i<m; i++ )
{
delete [] arrary[i];
}
delete [] array;
- 实例
生成一个3行4列的动态数组,令 a[i][j]=i+j,最后输出数组。
#include<iostream>
using namespace std;
int main()
{
int m=3,n=4;
// 动态分配内存
int **a=new int *[m];
for(int i = 0;i<n;i++)
a[i] = new int[n];
// 为数组赋值
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
a[i][j]=i+j;
}
// 输出数组
for(int i=0;i<n;++i)
{
for(int j=0;j<n;++j)
{
cout<<a[i][j];
if (j==n-1)
cout<<endl;
else
cout<<" ";
}
}
//释放申请的堆内存
for(i=0; i<m; i++){
delete [] a[i];
}
delete [] a;
return 0;
}
2.3 三维数组
- 基本语法:
通过动态内存分配,创建一个三维的 mxnxh 数组。
// 动态分配内存
int ***array;
array = new int **[m];
for( int i=0; i<m; i++ )
{
array[i] = new int *[n];
for( int j=0; j<n; j++ )
{
array[i][j] = new int [h];
}
}
//释放内存
for( int i=0; i<m; i++ )
{
for( int j=0; j<n; j++ )
{
delete[] array[i][j];
}
delete[] array[i];
}
delete[] array;
- 实例:
#include <iostream>
using namespace std;
int main()
{
int i,j,k; // p[2][3][4]
int ***p;
p = new int **[2];
for(i=0; i<2; i++)
{
p[i]=new int *[3];
for(j=0; j<3; j++)
p[i][j]=new int[4];
}
//输出 p[i][j][k] 三维数据
for(i=0; i<2; i++)
{
for(j=0; j<3; j++)
{
for(k=0;k<4;k++)
{
p[i][j][k]=i+j+k;
cout<<p[i][j][k]<<" ";
}
cout<<endl;
}
cout<<endl;
}
// 释放内存
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
{
delete [] p[i][j];
}
delete [] p[i];
}
delete [] p;
return 0;
}
【 3. 对象的动态内存分配 】
- 对象的动态内存分配与简单的数据类型类似。
- 实例:
为一个包含四个 Box 对象的数组分配内存,构造函数将被调用 4 次,同样地,当删除这些对象时,析构函数也将被调用相同的次数(4次)。
#include <iostream>
using namespace std;
class Box
{
public:
Box()
{
cout << "调用构造函数!" <<endl;
}
~Box()
{
cout << "调用析构函数!" <<endl;
}
};
int main( )
{
Box* myBoxArray = new Box[4];
delete [] myBoxArray; // 删除数组
return 0;
}
【 4. 其他内存管理函数 】
4.1 malloc() 、free()
- 头文件:
C:#include <stdlib.h>
C++ :#include <cstdlib>
- malloc() 函数 从堆(heap)中分配一块指定大小的内存区域,并返回一个指向这块内存的指针。malloc 分配的内存不会被自动初始化,它包含未定义的值。基本语法:
- size:需要分配的内存大小(以字节为单位)。
- 成功时,返回指向分配内存的指针, void* 类型,使用时通常需要进行 类型转换 。
失败时,返回 NULL 或 nullptr(在 C++11 及以后版本中)。
void* malloc(size_t size);
- free() 函数 释放 指针 指向的存储空间,被释放的空间通常被送入可用存储区池。使用 malloc 分配的内存必须使用 free 函数 来释放,不能使用 C++ 的 delete 运算符 。释放内存后,将指针设置为 NULL 是一个好习惯,可以防止野指针问题。基本语法:
- ptr:指向要释放的内存的指针。
- 无返回值。
void free(void* ptr);
- 例程
int *array = (int *)malloc(10 * sizeof(int));// 申请1个足够存储10个整数的内存
free(array); //释放该内存
- malloc/free 和 new/delete 对比
在 C 语言中的malloc() 函数,在 C++ 中仍然存在,但建议尽量 不要使用 malloc() 函数 。
malloc/free | new/delete |
---|---|
malloc/free 是C/C++的标准库 函数 | new/delete是C++的 运算符 |
malloc申请的堆区 内存不会初始化 | new是 内存自动初始化 |
malloc需要 用户计算好字节数来申请空间 | new 自动计算分配空间 |
malloc函数 返回值是void*,在使用时需要进行类型强制转换 | 在 new 后面直接写明类型 |
malloc 申请内存失败会返回一个NULL ,在使用的时候必须进行判空 | new 申请内存失败后会抛出异常 |
对于自定义数据类型,malloc/free只开辟空间(没有初始化),不会调用构造/析构函数 | 对于自定义数据类型,new不只是分配了内存,它还 创建了对象 。申请、销毁时会触发构造/析构函数。 |
malloc/free 都是程序员手动从 堆区 申请,然后手动释放 | new/delete都是程序员手动从 堆区 申请,然后手动释放 |
- 实例
#include <iostream>
#include <cstdlib> // 包含malloc函数的头文件
using namespace std;
int main() {
// 分配足够存储10个整数的内存
int *array = (int *)malloc(10 * sizeof(int));
// 检查分配是否成功
if (array == NULL ) {
// 如果分配失败,处理错误
return -1;
}
// 使用分配的内存
for (int i = 0; i < 10; ++i) {
array[i] = i*i; // 初始化数组元素
}
// 打印数组元素
for (int i = 0; i < 10; ++i) {
cout << "array[" << i << "]= " << array[i] << endl;
}
// 释放分配的内存
free(array); // 注意:只能使用free来释放malloc分配的内存
return 0;
}
4.2 memset 内存初始化
- 头文件
C:#include <string.h>
C++:#include <string>
- memset()函数 是 C 语言标准库中的一个函数,用于 将一块内存区域的前 n 个字节中的每个字节设置为指定的值 。基本语法:
- ptr:指向要填充的内存块的指针。
- value:要设置的值,通常是一个介于 0 到 255之间的整数 ,因为 memset 一次只设置一个字节大小。
- num:要 设置的字节数。
- 返回指向大小为 num 内存块的原始指针 ptr,如果错误发生返回NULL。
void* memset(void* ptr, int value, size_t num);
- 实例
#include <stdio.h>
#include <string.h> // 包含 memset 函数的头文件
int main() {
char buffer[5]; // 声明一个足够大的字符数组
// 使用 memset 初始化 buffer 的前 5 个字节为 '6'
memset(buffer, '6', 5);
// 打印
printf("Initialized buffer with '6':\n");
for (int i = 0; i < 5; ++i) {
printf("%c ", buffer[i]);
}
return 0;
}