目录
5、为什么有了malloc/free,后还需要new/delete
1、c++中内存的分布情况
(1)、栈:由编译器管理分配和回收,用于存放函数参数,局部变量等等
(2)、堆:由程序员自己管理,主要通过new, delete, malloc, free进行分配和回收,空间比较大,但有可能会存在内存泄漏和空闲碎片的情况
(3)、全局/静态区:分为初始化和未初始化两个相邻的区域,存储初始化和未初始化的全局变量和静态变量
(4)、常量区:用于储存常量,一般不允许修改
(5)、代码区:用于存放程序的二进制代码
2、栈和堆的区别
栈:由编译器进行管理,在需要的时候分配空间,在不需要的时候自动回收空间,一般用于存放函数参数和局部变量,连续的存储空间。一般入栈顺序是从右到左入栈。
堆:由程序员自己进行管理,在需要时通过new或malloc申请分配空间,在不需要的时候通过delete或free回收空间。如果不回收释放,则会存在内存泄漏的问题。不连续的空间。实际上,在分配内存时,系统中有一个空闲的内存链表,当程序员申请时,系统会遍历空闲链表,找到第一个内存空间大于或等于申请的空间分配给程序,一般在分配内存时,也会在内存头部写入内存的大小,目的是方便delete回收。
3、C++如何申请空间
C/C++主要是在堆区申请空间,主要通过new和malloc申请,然后通过delete或free释放回收内存。
4、new和malloc的区别
(1)、new和delete是C++关键字,而malloc和free是C语言库函数。都用于申请动态内存和释放内存。new的底层实现,也是基于malloc实现的。
(2)、new在调用时,是先分配内存,再调用构造函数,delete在调用时先使用析构函数,再释放内存,而malloc只分配内存,free只释放内存。故而,new比malloc更安全,因为,他会调用构造和析构函数。
(3)、new申请内存不需要计算申请内存的大小,且不需要强制类型转换,其返回的类型就是对应申请类型的指针,而malloc申请时,需要手动计算申请内存的大小,并且还需要强制类型转换为需要的类型,因为malloc返回的类型为 void*。
(4)、new申请内存并初始化,malloc申请内存,但不初始化。
(5)、new申请内存失败,返回NULL,而malloc申请内存失败,则返回异常。
5、为什么有了malloc/free,后还需要new/delete
因为对于非内部数据类型而言,光malloc/free并不能满足动态对象的要求,对象在创建和消亡时,会自动调用构造函数和析构函数,而malloc/free是库函数而非运算符,并不在编译器的控制权限内,故不能把构造和析构的任务交给malloc/free,故而有了new/delete。
6、函数传递参数的几种方式
(1)、值传递:形参是实参的拷贝,形参的改变并不影响实参
(2)、指针传递:也是值传递的一种,不过形参接收的是实参的地址,对形参所指对象进行操作,及等价于对实参的操作
(3)、引用传递:实际上就是把引用对象的地址放在了所开辟的栈空间中,函数对其形参的操作可以直接映射到实参上
7、C++中指针参数的传递和引用参数的传递
(1)、指针参数传递:本质是也是值传递,只不过传递是地址值,被调函数的形参被作为被调函数的局部变量处理,会在栈中开辟空间,用来存放主调函数传入的实参值拷贝值,及实参的一个副本。故当形参的指向没有变化时,对所指向的操作既是对实参的操作,若形参指向改变,则无法操作实参
(2)、引用参数传递:被调函数的形参也是作为被调函数的局部变量,会在栈区申请内存空间,但存放的是实参变量的地址,故而对形参的任何操作都会直接影响到实参,即可以通过栈中的地址找到实参变量。
(3)、两者的不同:虽然两者都是被调函数栈空间上的一个局部变量,但是对于任何引用参数的处理,都会通过间接寻址的方式操作到实参,而指针参数的传递,则是,如果改变了被调函数中的指针地址,则形参的操作无法再影响到实参。因为引用对象的指向不可以更改,而指针的指向可以更改。
8、指针和引用的区别
(1)、指针和引用都是一种内存地址的概念,区别在于,指针是一个实体,而引用只是一个别名。
(2)、指针:指针指向的是一块内存地址,所指向的内容是内存的地址,但指针所指向的值是可以改变的,允许拷贝和赋值,有const和非const的区别,可以为空,sizeof指针得到的也是指针类型的大小。
(3)、引用:对于引用而已,引用只是一块内存的别名,引用必须在定义时绑定到一块内存上,即引用必须初始化,后续不可更改,也不能为空,且没有const和非const的区别。sizeof引用得到的是所初始化对象的大小。
(4)、指针必须在解引用后才能对对象进行操作,而引用可以直接对对象进行操作。做为参数来说,指针实质上是值传递,传递的是地址在,而引用实在上是地址传递,传递的变量是地址值。
8.1、指针和数组的区别
(1)、数组是用于存储多个相同类型的数据集合,数组名是首元素的地址,sizeof的大小不一定。
(2)、指针:相当于一个变量,存储的是内存中的地址,sizeof的大小固定。
(3)、区别:同类型的指针可以相互赋值,而数组不行,数组只能一个一个元素的赋值或拷贝。
(4)、数组所占内存空间的大小:sizeof(数组名)/sizeof(数组数据类型);指针所占内存空间的大小:sizeof(指针名),在32位系统中占4字节,在64位系统中占8字节。
9、函数指针
(1)、定义:函数指针就是指向函数的指针,函数指针首先就是一个指针,该指针指向的是一个具体的函数,在编译时,每个函数都有一个入口地址,函数指针所指向的就是该函数的入口地址。有了指向函数的指针后,后面就可以用该函数指针调用该函数。
(2)、用途:调用函数或做为函数的参数。
10、野指针和悬挂指针,并如何避免
(1)、野指针:就是没有经过初始化的指针。
(2)、悬挂指针:就是最初指向的内存被释放后,未被置空的指针。
(3)、无论是野指针还是悬挂指针,所指向的都是一块无效内存的指针,访问无效内存,将导致程序编译出错。
(4)、避免:对于野指针的避免,就是在定义指针后且在使用之前对指针进行初始化,或用智能指针。对于悬挂指针的避免,就是在内存释放后,即使的吧指针置空或调用智能指针。
11、const修饰指针
(1)const int *p = &a 常量指针,指向可以改,但指向的值不可以改。
(2)int * const p = &a 指针常量,指向不可以改,但指向的值可以改
(3)const int * const p = &a const同时修饰指针和常量,指向和值都不可以改。
12、static定义静态变量
(1)、作用:控制变量的存储方式和作用域(生命周期和作用范围)
(2)、static修饰局部变量:对于一般局部变量而已,一般存放在栈区,且局部变量的生命周期在所包含的语句块结束后而结束,而通过static修饰后的局部变量,则该局部变量存放在静态区,生命周期会一直延续到整个程序执行结束后而结束,但其作用域未发生改变,作用的还是其所在语句块中。
(3)、static修饰全局变量:对于全局变量,一般存放在全局区,能够被整个程序所访问到,同时也能被同一个工程中的其他源文件所访问到(但是需添加extern声明),通过static修饰过后,则该全局变量存放在静态区,且也改变了该变量的作用域,通过static修饰后,该全局变量只能在本文件中被访问到,而其他文件访问不到。
ÿ