一.C/C++内存分布
1. 具体分布
(1)栈
函数栈帧的开辟,非静态局部变量/返回值/函数参数,它的特点是栈是向下增长
(2)数据段(静态区)
全局数据,static修饰的变量
(3)代码段(常量区)
存放常量例如字符串,可执行代码
(4)堆
用来动态申请内存的。它是向上增长的
(5)内存映射区
内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口 创建共享共享内存,做进程间通信
(6)内核空间
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);
}
1.
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区)
globalVar在哪里?__数据段__ staticGlobalVar在哪里?__数据段__
staticVar在哪里?__数据段__ localVar在哪里?__栈__
num1 在哪里?___栈_
char2在哪里?__栈__ *char2在哪里?_栈__
pChar3在哪里?_栈___ *pChar3在哪里?__代码段__
ptr1在哪里?__栈__ *ptr1在哪里?___堆_
2. 填空题:
sizeof(num1) = __40_;
sizeof(char2) = ___5; strlen(char2) = _4_;(不包括'/0')
sizeof(pChar3) = __8/4__; strlen(pChar3) = _4_;(不包括'/0')
sizeof(ptr1) = __8/4__;
3. sizeof 和 strlen 区别
1.sizeof 是操作符,不是函数,根据类型计算的内存大小,它可以计算对象,结构体等,
2.strlen 是计算字符串长度的函数,遇到‘/0’结束,返回字符串的长度(不包括‘/0’),而sizeof在计算字符串内存大小包括‘/0’.
二.C语言的内存管理方式
1.malloc/realloc/calloc/free
(1)malloc
用来动态开辟内存,以字节为单位,如果开辟失败,返回空指针(NULL)
(2)realloc
如果我们开辟的空间不够,可以使用realloc来扩容,如果realloc第一个参数为空,可以理解为和malloc作用相同。
(3)calloc
开辟空间的同时,会初始化为0
void Test ()
{
int* p1 = (int*) malloc(sizeof(int));
free(p1);
int* p2 = (int*)calloc(4, sizeof (int));
int* p3 = (int*)realloc(p2, sizeof(int)*10);
// 这里需要free(p2)吗?realloc在进行扩容的时候,它自己会释放掉原来的空间,不需要我们手动free
free(p3 );
}
三.C++内存管理方式
C++是面对对象,而C语言没有对象这个概念。如果沿用C语言方式来开辟对象的空间,这又涉及到对象的初始化问题,我们知道类实例化的时候,编译器会自动调用构造函数进行初始化,所以有了new和delete。
/new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间 还会调用构造函数和析构函数
1.new 和delet
(1)自定义类型和内置类型
//内置类型
int *pyt=new int(3);//申请一个int大小,初始化为3
int* ptr2 = new int[3] {1, 2, 3};//连续申请3个int,分别初始化
delete pyt;
delete []ptr2;//释放连续的空间配合[]
//自定义类型
MyQL* ptr3 = new MyQL(1);//申请一个对象(MyQL)空间同时调用构造函数
delete ptr3;
MyQL* ptr4 = new MyQL[3]{ 1,2,3 };//连续申请3个对象空间,调用3次构造函数(1,2,3会隐式转换)
delete []ptr4;//释放连续空间,调用析构函数完成对象资源清理,然后在释放对象的空间
void Test()
{
// 动态申请一个int类型的空间
int* ptr4 = new int;
// 动态申请一个int类型的空间并初始化为10
int* ptr5 = new int(10);
// 动态申请10个int类型的空间
int* ptr6 = new int[3];
delete ptr4;
delete ptr5;
delete[] ptr6;
}
注意:申请和释放单个元素的空间,使用new和delete操作符,申请和释放连续的空间,使用 new[]和delete[],注意:匹配起来使用
(2)new和delete的底层实现原理
new和delete是操作符,实际调用的是两个全局函数
new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是 系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过 operator delete全局函数来释放空间。实际上申请空间的是malloc函数,释放使用的是free函数。可以理解为new和delete是malloc和free的封装。
operator new[]----->operator new------>malloc
operator delete[]----->operator delete------>free
(3)delete/new和malloc/free区别
1.malloc/free是函数,delete/free是操作符
2.malloc申请的空间要计算申请空间的大小,而new只需要类型,如果申请多个对象只需在[]说明个数
3.mallo申请空间不会初始化,new会初始化
4.malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
5.malloc如果申请失败返回空指针,new失败抛出异常
6.申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new 在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成 空间中资源的清理
四.定位new表达式
new (place_address) type或者new (place_address) type(initializer-list) place_address必须是一个指针,initializer-list是类型的初始化列表
1.定位new表达式可以通过它显示调用构造函数
如果我们申请了对象的空间但是又没有初始化它,那么我们就可以通过定位new表达式,完成初始化工作,它最大的用处是内存池的对象的初始化
class MyQL {
public:
MyQL(int n)
:next(nullptr)
,date(n)
{
}
private:
MyQL* next;
int date;
};
int main()
{
MyQL* ptr = (MyQL*)malloc(sizeof(MyQL));
//不可以这样ptr->MyQL(1)
new(ptr)MyQL(1);
return 0;
}