类和对象——C/C++中的内存管理

一、C/C++的内存分布(从实例出发)

C和C++的内存分布上有很多都是相类似的,我们可以在C语言的基础上进行对比学习。首先我们先来看一段代码和填空题。

1.内存的虚拟空间地址

在分析题目之前,我们先来了解一下选择题的选项分别是指什么。
在这里插入图片描述
这是虚拟空间地址图,我们从这里就可以清晰看到各个选项所在的位置。
首先是栈和堆,这也是我们最熟悉的,栈是静态开辟的空间,是由编译器自己进行空间开辟,速度较快,但是程序员不能对其进行操控。按地址的数值大小,栈是向下进行空间开辟的。
相应的就是堆,堆是进行动态内存开辟,是由程序员自己操控进行,但是速度较静态分配慢,堆是向上进行空间开辟的。
接着就是数据段和代码段,这两个空间也很重要,数据段也叫静态存储区,所以就是用来存储静态变量和全局变量的,注意未初始化的变量是存放在BSS段。
然后是代码段,用来存放程序代码主体,函数主体,常量等等,要注意这里存放的都是二进制。

2.例题

int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
 static int staticVar = 1;
 int localVar = 1;
 int num1[10] = { 1, 2, 3, 4 };
 char char2[] = "abcd";
 const char* pChar3 = "abcd";
 int* ptr1 = (int*)malloc(sizeof(int) * 4);
 int* ptr2 = (int*)calloc(4, sizeof(int));
 int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
 free(ptr1);
 free(ptr3);
}

在这里插入图片描述

  • 选择题:
    我们可以看到在函数体外,定义了两个变量,在函数体外定义的变量就是全局变量,所以globalVar和staticGlobalVar都是在全局中定义的,但staticGlobalVar同时也是静态成员变量,不过全局变量和静态变量都是存在于数据段的,所以我们选择C。
    接着,staticVar和上面类似,仍然是一个静态变量,是一个静态局部变量,所以他仍然存放在数据段,选择C。localVar是一个普通的局部变量,是一个临时变量,存放于栈区,选择A。
    后面是num1,这是在函数内部申请的一个临时的数组,所以也是临时变量,存放于栈区,选择A。紧接着char2是一个字符数组,而数组名在这里是指首元素的地址,而存放地址的指针变量也是一个临时变量,存放于栈区,选择A。
    要想知道char2在哪,我们要先知道char2在哪,首先“abcd”这个字符串是一个常量,存放于代码段,但是这个数组是一个临时变量,当常量要赋值给一个变量时,会在栈区进行一次复制,将这个常量复制到栈区,然后再赋值给变量,如果是地址,那也是这份在栈区拷贝得到的变量地址。于是这里再看char2就很简单了,*char2是字符串的首字符,并且是在栈区拷贝得来的首字符,所以这题选择A。
    pChar3就和上面的临时数组有了不同的地方,pChar3因为是const修改的,所以指向不能发生改变,于是就指向了代码段的常量“abcd”。所以pChar3在栈区,选择A,但是解引用之后就是首元素了,所以是在代码段,选择D。
    ptr1是一个指针,临时变量,所以也是存在于栈区,选择A。但是解引用之后,就相当于是动态开辟的内容,这些内容是存在于堆区的,选择C。
  • 填空题:
    num1是有10个int类型变量的数组,所以大小为40byte。
    char2是指向一个字符串,而字符串的大小问题有一个点需要注意,就是每一个字符串之后,都有一个隐藏的‘\0’,用来表示结束,所以char2的大小就是4个字符加‘\0’,为5byte。
    strlen(char2)就是计算char2中的字符个数,因为strlen是遇到‘\0’就会停止计数,并且不会计算‘\0’,所以是4byte。
    pChar3的大小也取决于我们的系统,所以这个大小应该是4\8byte。然后pChar3的strlen和char2的类似,也是4byte。
    ptr1也是指针,所以大小也是4\8byte。
  • 论述题:
    strlen和sizeof最本质的区别就是前者是一个函数,而后者是一个操作符。
    sizeof的返回值是size_t,strlen的是int,并且strlen只能是计算字符串的长度,但是sizeof还可以输入类型,计算类型所占内存的大小。
    在strlen中输入数组时,就会变成指针,但是在sizeof中还是那个数组,等等。

二、C语言中的动态内存管理

动态内存分配最常用的函数其实就是malloc,calloc,realloc,free。
在之前的学习中,我们也已经对malloc有了一定的了解,那么我们就重点说说malloc,calloc,realloc这三者的区别。

  • malloc和realloc都只是进行空间的开辟,calloc在空间开辟之后又进行了赋0的初始化,这个功能前两者通过memset也可以实现。
  • malloc和realloc都是对整块空间进行开辟,返回的也是整块的,calloc返回的是一个数组,也就是分割好的。
  • malloc和calloc都是在空间中单独找一块进行空间申请,realloc是在目标空间之后紧接着进行申请。

三、C++中的动态内存管理

在C++中,也有着不同于C语言,但又和C语言相似的动态内存管理方式。为了方便C++对非内置类型的自定义类型进行动态内存管理,C++使用new\delete操作符来进行内存管理。

1.new\delete管理内置类型

void Test()
{
  // 动态申请一个int类型的空间
  int* ptr4 = new int;
  
  // 动态申请一个int类型的空间并初始化为10
  int* ptr5 = new int(10);
  
  // 动态申请10个int类型的空间
  int* ptr6 = new int[10];
  delete ptr4;
  delete ptr5;
  delete[] ptr6;
}

从这段代码中我们可以看出一下几个注意事项

  • 如果单个空间进行动态开辟,要分为是否进行初始化,如果不进行初始化,则new+类型即可,如果要进行初始化,则要new + 类型的后面再加一个(初始值)。
  • 如果是对连续的空间进行动态开辟,则在类型后面加一个[对象个数],但是无法对多个连续空间进行初始化。
  • 此外我们还发现,new\delete和new[ ]\delete[ ]这两对操作符是前后呼应的,一个是进行开辟,一个是进行回收。

2.new\delete管理自定义类型

class A
{
public:
 A(int a = 0)
 : _a(a)
 {
 cout << "A():" << this << endl;
 }
 ~A()
 {
 cout << "~A():" << this << endl;
 }
private:
 int _a;
};
int main()
{
 A* p1 = (A*)malloc(sizeof(A));
 A* p2 = new A(1);//执行时会调用一次析构函数
 free(p1);
 delete p2;
 // 内置类型是几乎是一样的
 int* p3 = (int*)malloc(sizeof(int)); // C
 int* p4 = new int;//内置类型不进行初始化时,和malloc没有太大的区别
free(p3);
delete p4;
 A* p5 = (A*)malloc(sizeof(A)*10);
 A* p6 = new A[10];//动态开辟多个元素时,会按元素个数调用构造函数,并进行初始化
 free(p5);
 delete[] p6;
 return 0;
}

在这里插入图片描述

这是代码输出的所有内容,一共是11遍构造和析构。

我们知道了,new和malloc最大的区别就是new在动态管理自定义类型时,会调用类的构造函数,相应的delete和free最大的区别也是这样,delete会自动调用析构函数。

四、常见论述题

1.malloc\free和new\delete的区别

  • 两对最本质的区别就是malloc\free是函数,new\delete是操作符,new\delete这两个操作符的底层是使用malloc\free这两个函数进行实现的。
  • new可以使用构造函数进行初始化,malloc不能进行初始化,相应的delete可以使用析构函数对类进行清理,但free不能调用析构函数。
  • malloc需要输入计算好的内存大小,但是new直接在后面加上类型即可,如果是多个元素则需要在类型后面加上[ ]中间是元素个数。
  • 输出的malloc是void*类型的指针,需要进行强转才可以接收,new不需要进行强转,因为new后面就是相应的类型。

2.简单介绍内存泄漏及其危害

内存泄漏不是指物理意义上内存的丢失,而是指在内存使用过后没有进行及时的释放,导致对这块内存失去控制,造成了内存上的浪费。
内存泄漏的危害:长期运行的程序出现内存泄漏,影响很大,如操作系统、后台服务等等,出现内存泄漏会导致响应越来越慢,最终卡死。

创作不易,感谢阅读。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值