C++ 后端开发面试知识点汇总之编译与底层(三)

1、C++源文件从文本到可执行文件经历的过程

对于c++源文件,从文本到可执行文件一般需要四个过程:

  • 预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换、,生成预编译文件。
  • 编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件。
  • 汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件。
  • 链接阶段:将多个目标文件及所需要的库连接成最终可执行目标文件。

2、include头文件的顺序以及栓引号和尖括号的区别

(1)Include头文件的顺序:对于include的头文件来说,如果在文件a.h中声明一个在文件b.h中定义的变量,而不引用b.h。那么要在a.c文件中引用b.h文件,并且要先引用b.h,后引用a.h,否则汇报变量类型未声明错误。

(2)双引号和尖括号的区别:编译器预处理阶段查找头文件的路径不一样。

  • 对于使用双引号包含的头文件,查找头文件路径的顺序为:

当前头文件目录;

编译器设置的头文件路径(编译器可使用-I显式指定搜索路径);

系统变量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的头文件路径;

  • 对于使用尖括号包含的头文件,查找头文件的路径顺序为:

编译器设置的头文件路径(编译器可使用-I显式指定搜索路径);

系统变量CPLUS_INCLUDE_PATH/C_INCLUDE_PATH指定的头文件路径;

3、malloc的原理,另外brk系统调用和mmap系统调用的作用

        malloc函数用于动态内存分配。为了减少内存碎片和系统调用的开销,malloc采用内存池的方式,先申请大块内存作为堆区,然后将堆区分成多个内存块,以块作为内存管理的基本单位。当用户申请内存时,直接从堆区分配一块适合的空闲块。malloc采用隐式链表结构将堆区分成连续的大小不一的块,包含已分配块和未分配块;同时malloc采用显示链表结构来管理所有的空闲块,即使用一个双向链表将空闲块连接起来,每一个空闲块记录了一个连续的、未分配的地址。

        当进行内存分配时,malloc会通过隐式链表遍历所有的空闲块,选择满足要求的块进行分配;当进行内存合并时,malloc采用边界标记法,根据每个块的前后块是否已经分配来决定是否进行块合并。

        malloc在申请内存时,一般会通过,brk或者mmap系统调用进行申请。当申请的内存小于128K时,会使用系统函数brk在堆中分配;当申请大于128K时,会使用系统函数mmap在映射区分配。

   

4、如何判断内存泄漏

内存泄漏通常是由于调用了malloc/new等内存申请的操作,但是确实扫了对应的free/delete。为了判断内存是否泄露,一方面可是使用linux环境下的内存泄漏检查工具valgrind;另一方面我们在写代码时可以添加内存申请和释放的统计功能,统计当前申请和释放内存是否一致,一次判断有无内存泄漏。

5、段错误

(1)什么是段错误:一旦一个程序发生了越界访问,cpu 就会产生相应的保护,于是段错误就会出现。简单来说,段错误就是访问了不可访问的内存,这个内存要么是不存在的,要么是受到系统保护的,还有可能是缺少文件或文件损坏。

(2)段错误产生的原因:

  • 访问不存在的内存地址。例如对空指针取值
  • 访问受系统保护的地址。例如试图修改常量的内容
  • 堆栈溢出(如错误的递归)
  • 内存越界(数组越界,变量类型不一致)

6、内存泄漏

(1)概念:内存泄漏是指程序代码中动态申请的内存,由于某种原因,使用后没有被释放掉,造成的内存的浪费。

(2) 常见的几种内存泄漏:

  • malloc/new 申请的内存没有使用free/delete释放;
  • 动态申请的数组,使用delete删除(正确应该是delete[]);
  • 基类中存在指针并且动态申请内存时,没有将析构函数定义为虚函数;
  • 类中存在指针并且动态申请内存时,没有重载拷贝构造函数(默认拷贝构造函数为浅拷贝,可能会导致内存泄漏)。
  • 类中存在指针并且动态申请内存时,没有重载赋值运算符(默认拷贝构造函数为浅拷贝,可能会导致内存泄漏)

7、请你来说一下共享内存相关api

Linux允许不同进程访问同一个逻辑内存,提供了一组API,头文件在sys/shm.h中。

1)新建共享内存shmget

int shmget(key_t key,size_t size,int shmflg);

key:共享内存键值,可以理解为共享内存的唯一性标记。

size:共享内存大小

shmflag:创建进程和其他进程的读写权限标识。

返回值:相应的共享内存标识符,失败返回-1

2)连接共享内存到当前进程的地址空间shmat

void *shmat(int shm_id,const void *shm_addr,int shmflg);

shm_id:共享内存标识符

shm_addr:指定共享内存连接到当前进程的地址,通常为0,表示由系统来选择。

shmflg:标志位

返回值:指向共享内存第一个字节的指针,失败返回-1

3)当前进程分离共享内存shmdt

int shmdt(const void *shmaddr);

4)控制共享内存shmctl

和信号量的semctl函数类似,控制共享内存

int shmctl(int shm_id,int command,struct shmid_ds *buf);

shm_id:共享内存标识符

command: 有三个值

IPC_STAT:获取共享内存的状态,把共享内存的shmid_ds结构复制到buf中。

IPC_SET:设置共享内存的状态,把buf复制到共享内存的shmid_ds结构。

IPC_RMID:删除共享内存

buf:共享内存管理结构体。

8、C++ STL 内存优化

C++ STL内存优化采用二级内存配置器。如果要分配的内存大于128B,则移交给第一级配置器处理。如果要分配的内存小于128byte,则以内存池管理:每次配置一大块内存,并维护对应的16个空闲列表,这16个空闲列表分别管理大小为8-128B的内存块。当用户申请的内存小于128B时,从空闲链表中查找对应大小的内存块,如果空闲列表中查找不到或者块数不够,则像内存池进行申请,一般一次申请20块,如果内存池空间足够,则取出内存加入空闲列表,如果不够分配20块,则分配最多块数,如果一块都无法提供,则把剩余内存挂到自由链表,然后像系统堆申请空间。,如果申请失败,则看看自由链表中还有没有可用的块,如果也没有,则调用一级空间配置器。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值