提纲:
👉 八股:
- 说说静态变量在代码执行的什么阶段进行初始化
- 给我介绍一下 静态全局变量、静态局部变量、全局变量、局部变量的特点,以及他们的应用场景。
- 你了解虚拟空间么 or 你了解C++的内存分布模型吗?
- 简单概述一下堆和栈的区别
- 为什么使用虚拟内存;其好处与坏处是什么?
- 申请堆内存时需要注意什么?
- 你了解内存泄漏么?
- malloc内存管理原理
- 了解内存碎片么?
- 了解野指针么?
- 用new和malloc申请内存时有什么区别?你了解他们的底层实现么?
- 什么是内存池
- 在使用指针的时候你都从哪些方面考虑?
- 初始化为0的全局变量在bss还是data
- 在1G的内存中,能成功分配4G的数组么
👉 代码:
C++八股内容:
1、说说静态变量在代码执行的什么阶段进行初始化
static int value //静态变量初始化语句
- 对于C语言: 静态变量和全局变量均在编译期进行初始化,及初始化发生在任何代码执行之前。
- 对于C++: 静态变量和全局变量仅当首次被使用的时候才进行初始化。
助记: 如果你使用过C/C++你会发现,C语言要求在程序的最开头声明全部的变量,而C++则可以随时使用随时声明;这个规律是不是和答案类似呢?
2. 给我介绍一下 静态全局变量、静态局部变量、全局变量、局部变量的特点,以及他们的应用场景。
-
先分析一下他们各自是什么(此处以静态变量进行讨论)
-
全局变量就是定义在函数外的变量。
-
局部变量就是函数内定义的变量。
-
静态变量就是加了static的变量。 例如:
static int value = 1
-
-
各自存储的位置:
-
全局变量,存储在常量区(静态存储区)。
-
局部变量, 存储在栈区。
-
静态变量,存储在常量区(静态存储区)。
-
注意: 因为静态变量都在静态存储区(常量区),所以下次调用函数的时候还是能取到原来的值。
-
-
各自初始化的值:
-
局部变量一般是不初始化的,
-
全局变量和静态变量,都是初始化为0的,有一个初始值。
-
如果是类变量,会调用默认构造函数初始化。
-
-
从作用域来考虑:
首先你需要清楚:C++里作用域可分为6种:全局,局部,类,语句,命名空间和文件作用域
-
全局变量(函数体外定义): 全局作用域,可以通过extern(引入C的那个)作用于其他非定义的源文件;都会一直存在,直到程序结束。
-
静态全局变量 : 全局作用域+文件作用域,所以无法在其他文件中使用。
-
局部变量: 局部作用域,比如函数的参数,函数内的局部变量等等;它从进入作用域遇到该变量的时候开始出现,在离开的时候销毁。
-
静态局部变量 : 局部作用域,只被初始化一次,直到程序结束。
-
-
各自的应用场景:
-
局部变量就是我们经常用的,进入函数,逐个构造,最后统一销毁。
-
全局变量主要是用来给不同的文件之间进行通信。
-
静态变量:只在本文件中使用,局部静态变量在函数内起作用,可以作为一个计数器。
-
例子:
void func(){ static int count; count ++; } int main(int argc, char** argv){ for(int i = 0; i < 10; i++) func(); }
-
3. 你了解虚拟空间么 or 你了解C++的内存分布模型吗?
引子:
-
首先你需要了解 物理内存:
- 物理内存实际上是 CPU中能直接寻址的地址线条数。由于物理内存是有限的,例如32位平台下,寻址的大小是4G,并且是固定的。内存很快就会被分配完,于是没有得到分配资源的进程就只能等待。当一个进程执行完了以后,再将等待的进程装入内存。这种频繁的装入内存的操作是很没效率的。
-
这就需要用到 虚拟内存 了。
- 在那个进程创建的时候,系统都会给每个进程分配4G的内存空间,这其实是虚拟内存空间。进程得到的这4G虚拟内存,进程自身以为是一段连续的空间,而实际上,通常被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,需要的时候进行数据交换。
-
关于虚拟内存与物理内存的联系,下面这张图可以帮助我们巩固。
虚拟内存机理及优点:
-
虚拟内存是如何工作的?
-
当每个进程创建的时候,内核会为进程分配4G的虚拟内存,当进程还没有开始运行时,这只是一个内存布局。实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射)。这个时候数据和代码还是在磁盘上的。当运行到对应的程序时,进程去寻找页表,发现页表中地址没有存放在物理内存上,而是在磁盘上,于是发生缺页异常,于是将磁盘上的数据拷贝到物理内存中。
-
另外在进程运行过程中,要通过malloc来动态分配内存时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。
-
可以认为虚拟空间都被映射到了磁盘空间中(事实上也是按需要映射到磁盘空间上,通过mmap,mmap是用来建立虚拟空间和磁盘空间的映射关系的)
-
-
利用虚拟内存机制的优点 ?
-
既然每个进程的内存空间都是一致而且固定的(32位平台下都是4G),所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际内存地址,这交给内核来完成映射关系
-
当不同的进程使用同一段代码时,比如库文件的代码,在物理内存中可以只存储一份这样的代码,不同进程只要将自己的虚拟内存映射过去就好了,这样可以节省物理内存
-
在程序需要分配连续空间的时候,只需要在虚拟内存分配连续空间,而不需要物理内存时连续的,实际上,往往物理内存都是断断续续的内存碎片。这样就可以有效地利用我们的物理内存
-
C++的内存分布模型:
-
从高地址到低地址,一个程序由内核空间、栈区、堆区、BSS段、数据段(data)、代码区组成。
-
可执行程序在运行时会多出两个区域:
-
堆区: 动态申请内存用。堆从低地址向高地址增长。
-
栈区: 存储 局部变量、函数参数值 。栈从高地址向低地址增长。是一块连续的空间。
-
-
在堆栈之间有一个 共享区(文件映射区)。
-
BSS 段:一块存放程序中未初始化 的全局变量和静态变量 的内存区域。
-
数据段data:一块存放程序中已初始化的全局变量和静态变量的内存区域。
-
代码段: 存放程序执行代码的一块内存区域。只读,不允许修改,代码段的头部还会包含一些只读的常量,如字符串常量字面值(注意:const变量虽然属于常量,但是本质还是变量,不存储于代码段)
-
在linux下size命令可以查看一个可执行二进制文件基本情况:
4.简单概述一下堆和栈的区别
-
申请方式及申请效率不同:
- 总结:栈申请的快,效率比堆高。
- 栈由系统自动分配,存放函数的参数值,局部变量的值;申请速度较快;而堆是人为申请开辟,也需要程序员手动释放,申请速度慢。
-
申请所得空间大小不同:
- 栈的空间大小并不大,一般最多为2M,超过之后会报Overflow错误。堆的空间非常大,理论上可以接近3G。(针对32位程序来说,可以看到内存分布,1G用于内核空间,用户空间中栈、BSS、data又要占一部分,所以堆理论上可以接近3G,实际上在2G-3G之间)。
就是上面那个图我画的 3G 和 1G 的分布。
-
缓存方式不同:
- 栈使用的是一级缓存, 它们通常都是被调用时处于存储空间中,调用完毕立即释放;堆则是存放在二级缓存中,速度要慢些。
-
是否会产生内存碎片(内存碎片是操作系统知识,不知道百度即可)
- 栈不会产生内存碎片,堆会;因为栈采用后进先出,不可能进入空弹出空,而堆是人为频繁调用new或者malloc,所以总会产生碎片。
此题总结:
1、申请方式的不同。 栈由系统自动分配,而