C/C++ 内存的动态申请与释放

一:C中的相关函数

需要包含头文件

  • C方式: #include <stdlib.h>
  • C++方式:#include <cstdlib>

1.void *malloc (unsigned size)

  • 申请size字节的连续内存空间,返回该空间首地址,对申请到的空间不做初始化操作
  • 如果申请不到空间,返回NULL
int *p = (int *)malloc(sizeof(int));
if (p == NULL){
    cout << "No Memory" << endl;
    return -1;
    }
//一维数组
int *p = (int *)malloc(10 * sizeof(int));  //申请10个int型空间
int i;
for(i = 0; i < 10; i++)
    p[i] = i;  //赋值
for(i = 0; i < 10; i++)
    cout << *(p + i) << endl;  //打印
//二维数组
int (*p)[4] = (int (*)[4])malloc(10 * 4 * sizeof(int));  

2.void *calloc(unsigned n, unsigned size)

  • 申请n*size字节的连续内存空间,返回该空间首地址,对申请到的空间做初始化为0
  • 如果申请不到,返回NULL
int *p = (int *)calloc(1,sizeof(int));
//一维数组
int *p = (int *)calloc(10,sizeof(int));  //申请10个int型空间
//二维数组
int (*p)[4] = (int (*)[4])calloc(10 * 4, sizeof(int));  

3.void *realloc(void *ptr, unsigned newsize)

  • 表示为指针ptr重新申请newsize大小的空间
  • ptr必须是malloc/calloc/realloc返回的指针
  • 如果ptr为NULL,则等同于malloc
  • 如果ptr非NULL,newsize为0,则等同于free,并返回NULL
  • 新老空间可能重合,也可可能不重合,若不重合,空间原有内容会被赋值到新空间,再释放原空间
  • 对申请到的空间不做初始化操作
  • 若申请不到,则返回NULL(此时原指针ptr不释放)

4.void free(void *p)

  • 释放p所指的内存空间(p必须是malloc/calloc/realloc返回的首地址
free(p);

二:C++中的相关运算符

1.用new运算符申请空间

如果申请不到空间,new缺省会抛出bad_alloc异常,需要用try-catch方式处理异常,也可以在new时加入nothrow来强制禁用抛出异常并返回NULL

int *p = new(nothrow) int;
if (p == NULL){
    cout << "No Memory" << endl;
    return 0;
    }
//一维数组
int *p = new int[10];  //申请10个int型空间
//二维数组
int (*p)[4];
p = mew int[10][4]

申请空间同时赋值:

int *p = new int(10);
int *p = new int[10] {1,2,3,4,5};  //申请10个int型空间

//{}前不加=,且[]内必须有数字
int (*p)[3];
p = new int[2][3] {{1,2,3},{4,5,6}};

char (*q)[6];
p = new char[2][6] {"Hello", "China"};

2.用delete运算符释放空间

delete []p;
//二维以上必须加[]

三:注意事项

  • 如果出现需要嵌套进行动态内存申请的情况,则按照从外到内的顺序 进行申请,范旭进行释放
#include <iostream>
#include <cstdlib>
using namespace std;

struct student {
	int num;
	char* name;
};

int main()
{
	student* s1;
	//申请
	s1 = new(nothrow) student;
	s1->name = new(nothrow) char[6];
	//赋值
	s1->num = 1001;
	strcpy(s1->name, "zhao");
	//释放
	delete[]s1->name;
	delete s1;
	
	return 0;
}
  • 在数据成员需要动态内存申请的情况下,一般在构造函数中进行动态内存申请,虚构函数释放申请的空间
#include <iostream>
using namespace std;
class Time{
    private:
        int hour;
        int minute;
        int sec;
        char *s;
    public:
        Time() {
            hour = minute = sec =0;
            s = new char[80];  //申请
            }
        ~Time(){
            delete []s;  //释放
            }
};

四:含动态内存申请的对象的赋值与复制

1.含动态内存申请的对象的赋值

  • 含义:将一个对象的所有数据成员的值对应赋给另一个对象的数据成员
  • 形式:
Time t1(14,15,23), t2;
t2 = t1;
  • 两个对象属于同一个类,通过赋值语句实现
  • 系统默认的赋值操作是将右对象的全部数据成员的值对应赋给左对象的全部数据成员(理解为整体内存拷贝,但不包括成员函数),在对象的数据成员无动态内存申请时可直接使用

若对象数据成员是指针并涉及动态内存申请,则需自行实现(通过=运算符的重载实现)

错误示例:在这里插入图片描述
在这里插入图片描述
解决方法:重载=

test &test::operator=(const test &t)
{
    a = t.a;
    b = t.b;
    delete []c;  //释放原空间
    c = new char[20];  //申请新空间
    strcpy(c, t.c);
    return *this;  //返回对象自身
}

2.含动态内存申请的对象的复制

  • 含义:建立一个新对象,其值与某个已有对象完全相同
  • 形式:
Time t1(14,15,23), t2(t1), t3 = t1;

赋值与复制的区别:

  • 赋值:已有对象 => 已有对象
  • 复制:已有对象 => 新对象
  • 系统的默认复制操作是将已有对象的全部数据成员的值对应赋给新对象的全部数据成员(理解为整体内存拷贝,但不包括成员函数),在对象的数据成员无动态内存申请时可直接使用
  • 若对象数据成员是指针并涉及动态内存申请,则需自行实现(通过重定义赋值/拷贝构造函数来实现)

复制构造函数:

  • 类名(const 类名 &引用名)
  • 用一个对象的值去初始化另一个对象
  • 若不定义复制构造函数,则系统自动定义一个,参数为const类型引用,函数体为对应成员的内存拷贝
  • 若定义的复制构造函数,则系统缺省定义消失
  • 复制构造函数和普通构造函数的地位平等,调用其中一个后就不再调用其他构造函数

复制构造函数的调用时机:

  • 用已有对象初始化一个新建立的对象时
  • 函数形参为对象,实参向形参单向传值时
  • 函数的返回类型是对象时
  • PS:形参为引用时不调用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
错误示例:
在这里插入图片描述
解决方法:自己定义复制构造函数

test::test(const test &t)
{
    a = t.a;
    b = t.b;
    c = new char[20];
    strcpy(c, t.c);
}

五:浅拷贝与深拷贝

1.浅拷贝:

  • 只复制对象的指针,而不复制对象自身
  • 新旧对象共享内存
  • 修改其中一个的值,另一个也会随之改变
  • 两个对象是联动的

2.深拷贝

  • 复制对象自身
  • 新旧对象分别占用不同内存
  • 修改其中一个的值不会影响另一个
  • 两个对象是完全独立
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值