1.初步了解内存中各个区间存储的数据特征
1.栈区:存储一些局部变量、函数参数、返回值等,跟函数栈振有关,出了作用域,生命周期结束。
2.堆区:用于动态开辟空间,如果不主动销毁空间,则程序运行结束,生命周期结束。
3.数据段(静态区):static修饰的静态变量和全局变量,程序运行结束,生命周期结束。
4.代码段(常量区):可执行的代码和常量。
练习
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); }
选项 : A . 栈 B . 堆 C . 数据段 ( 静态区 ) D . 代码段 ( 常量区 )globalVar 在哪里? __1__ staticGlobalVar 在哪里? __2__staticVar 在哪里? __3__ localVar 在哪里? __4__num1 在哪里? __5__char2 在哪里? __6__ * char2 在哪里? __7_pChar3 在哪里? __8__ * pChar3 在哪里? __9__ptr1 在哪里? __10__ * ptr1 在哪里? __11__
1.C 2.C 3.C 4. A 5.A6.A 7. A 8.A 9.D 10.A 11.B
结合上图可以得知,cha2其本身存放在栈区,指针指向栈区中数组首元素的地址,再将静态区中"abcd"赋值给数组,所以*char指向的元素在栈区!!!pChar3其本身存放在栈区,指向静态区中存放"abcd"字符串的首地址!!!
2.c语言实现内存管理
2.1malloc
在堆上开一快符合你预期大小的一块空间,并且返回指向该地址空间的指针
void* malloc (size_t size);
size:开多大的空间,单位是字节
2.2realloc
如果malloc开辟出来的空间开少了,realloc可以在堆上重新开一块符合你预期大小的空间,并返回指向该空间的指针
void* realloc (void * ptr,size_t size);
ptr:初始空间的地址
size:将空间开辟到多大
2.3calloc
用于对开辟的空间进行初始化,calloc会将开辟的空间中每个元素初始化为0
void* realloc (size_t num,size_t size);
num:分配的元素数量
size:每个元素的大小
3.c++实现内存管理
3.1 new/delete new []/delete []
在c++中,有两对操作符new/delete、new []/delete [],他们的作用是负责开空间和释放空间!!!
new作用跟malloc类似,delete作用跟free类似
new和delete在面对自定义类型时会去调用构造函数和析构函数
new =先申请对象空间 再调用构造函数
delete = 先调用析构函数 再释放对象空间
3.1.1内置类型
当需要开一个整形的空间时。
int main()
{
//malloc
int* p1 = (int*)malloc(sizeof(int));
//new开空间用法跟malloc不同,但是作用相同,都是负责开空间!!!
int* p2 = new int;
//free
free(p1);
//delete
delete(p2);
return 0;
}
当需要开多个空间时 例如开10个int类型的空间 需要加上[]
int main()
{
int* p1 = (int*)malloc(sizeof(int)*10);
int* p2 = new int[10];
free(p1);
delete[](p2);
return 0;
}
3.1.2自定义类型
new和delete在面对自定义类型时会去调用构造函数和析构函数
new =先开空间 再调用构造函数
delete = 先调用析构函数 再释放空间
class Stack
{
public:
Stack(int capacity = 4)
{
_a = new int[capacity];
int _top = 0;
int _capacity = capacity;
}
~Stack()
{
delete (_a);
_a = nullptr;
_top = 0;
_capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
Stack* p2 = new Stack;
//new先开空间 再去调用构造函数
delete(p2);
//delete先调用析构函数,再去释放空间
return 0;
}
如果需要开10个Stack类型的空间 用new[] 和 delete[]
int main()
{
Stack* p2 = new Stack[10];
//new先开空间 再去调用构造函数
delete[](p2);
//delete先调用析构函数,再去释放空间
return 0;
}
3.2new/delete底层实现原理
3.2.1 全局函数 operator new/operator delete
要知道原理,我们就得先知道operatot new / operator delete 这两个全局函数
在c++中,有这样两个全局函数,他们的作用跟malloc、free类似,都是负责开空间和释放空间!!!
注意:operator new / operator delete 不是new/delete的重载,而是两个全局函数!!!
我们知道malloc申请空间失败的时候,会返回空。而operator new申请空间失败的时候则会抛异常。我们可以理解为operator是封装的malloc。
也就是说operator new 和mclloc除了申请空间失败的处理方法不同,其他的用法以及功能是相同的!!!
operator delete 可以理解为跟operator new对应,其用法和功能跟free完全一样!!!
int main()
{
Stack* p1 = (Stack*)malloc(sizeof(Stack));
free(p1);
//operator new底层用的是malloc
Stack* p2 = (Stack*)operator new (sizeof(Stack));
//operator delete底层用是free
operator delete(p2);
return 0;
}
3.2.2 new/delete 和 operator new/operator delete的关系
new = 1.申请对象空间 + 2.调用构造
delete = 1.调用析构 + 2.释放对象空间
在底层原理上,new的第一步申请对象空间底层就是调用operator new函数,
operator的第二步释放对象空间底层就是调用operator operator函数.
也就是说下面两段不同的代码,起到的作用都是相同的
int main()
{
Stack* p1 = new Stack;
delete (p1);
//等价于new
Stack* p2 = (Stack*)operator new (sizeof(Stack));
//定位new显示调用构造函数
new(p2)Stack;
//等价于delete
p2->~Stack();
operator delete(p2);
return 0;
}
我们到汇编语言的角度证明一下