《基础知识——编译与底层》

本文详细介绍了C++源文件从预处理到链接成为可执行文件的四个阶段,包括预处理、编译、汇编和链接。同时,讨论了头文件包含顺序的策略和双引号与尖括号的区别。深入探讨了malloc的内存管理,包括内存池和brk、mmap系统调用的作用。最后,阐述了C++的内存管理、内存分配方式、内存泄漏及其检测和处理方法,以及new和malloc的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 一个C++源文件从文本到可执行文件经历的过程?

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

1、预处理阶段:对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,生成预编译文件。

2、编译阶段:将经过预处理后的预编译文件转换成特定汇编代码,生成汇编文件

3、汇编阶段:将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件

4、链接阶段:将多个目标文件及所需要的库连接成最终的可执行目标文件

  • include 头文件的顺序以及双引号 “ ” 和尖括号 < > 的区别?

 

C++头文件的包含顺序研究】、【区别】

《C++编程思想》P432提到:头文件被包含的顺序是从“最特殊到最一般”。这就是,在本地目录的任何头文件首先被包含。然后是我们自己的所有“工具”头文件,随后是第三方库头文件,接着是标准C++库头文件和C库头文件。

【优点】:《C++编程思想》则很容易让你清楚知道你所定义的接口是否和系统库及第三方库发生冲突。

《Google C++ 编程风格指南》遵循的顺序“从一般到特殊”的原则:C标准库、C++标准库、其它库的头文件、你自己工程的头文件  中漏了最前面的一项:操作系统级别的头文件,比如上面的例子sys/types.h估计不能归入C标准库,而是Linux操作系统提供的SDK吧。因此我觉得更准确的说法应该是:OS SDK .h , C标准库、C++标准库、其它库的头文件、你自己工程的头文件

【优点】:能大量减少隐藏的头文件依赖。

加双引号表示,应用程序先在当前的文件夹里面寻找该头文件,若没有找到,再到系统文件夹里去找。一般加双引号多为自己编写的头文件。

加尖括号则表示,应用程序直接到系统文件夹去找该文件。这类多为系统头文件。

  • malloc的原理,另外brk系统调用和mmap系统调用的作用分别是什么?

【原理】:Malloc函数用于动态分配内存。

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

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

【申请内存】:Malloc在申请内存时,一般会通过brk或者mmap系统调用进行申请。

【brk】:其中当申请内存小于128K时,会使用系统函数brk在堆区中分配;

【mmap】:而当申请内存大于128K时,会使用系统函数mmap在映射区分配。

在C++中,虚拟内存分为代码段、数据段、BSS段、堆区、文件映射区以及栈区六部分。(??四区还是六区)

【代码段】:包括只读存储区和文本区,其中只读存储区存储字符串常量,文本区存储程序的机器代码。

【数据段】:存储程序中已初始化的全局变量和静态变量

【BSS段】:存储未初始化的全局变量和静态变量(局部+全局),以及所有被初始化为0的全局变量和静态变量。

【堆区】:调用new/malloc函数时在堆区动态分配内存,同时需要调用delete/free来手动释放申请的内存。

【映射区】:存储动态链接库以及调用mmap函数进行的文件映射

【栈区】:使用栈空间存储函数的返回地址、参数、局部变量、返回值

【内存分配】:在当前的Windows 32 位操作系统中默认内核空间和用户空间的比例是1:1(2GB 的内核空间,2GB 的用户空间),而在32 位Linux系统中默认的比例是1:3(1GB 的内核空间,3GB 的用户空间)。

内存分配方式】:静态存储区 栈 堆 的内存分配

1,从静态存储区域分配内存。程序编译的时候内存已经分配好了,并且在程序的整个运行期间都存在,例如全局变量。

2,在栈上创建。在执行函数时,函数内局部变量的存储单元可以在栈上创建,函数结束时这些存储单元自动被释放。
处理器的指定集中有关于栈内存的分配运算,因此效率比较高,但是分配的内存容量有限。

3,在堆上分配内存,亦称动态内存分配,程序在运行的时候用malloc函数或new运算符申请任意大小的内存,程序员
要用free函数或delete运算符释放内存。动态内存使用非常灵活,但问题也很多。
 

  • 什么是内存泄露?如何检测?如何处理?

内存泄漏(memory leak)是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。

简单地说就是申请了一块内存空间,使用完毕后没有释放掉。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。

【如何检测内存泄露】

  • 第一:良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度。使用了内存分配的函数,一旦使用完毕,要记得要使用其相应的函数释放掉。
  • 第二:将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表。
  • 第三:Boost 中的smart pointer。
  • 第四:一些常见的工具插件,如ccmalloc、Dmalloc、Leaky等等。

【如何避免】

智能指针。因为智能指针可以自动删除分配的内存。智能指针和普通指针类似,只是不需要手动释放指针,而是通过智能指针自己管理内存的释放。

【如何处理】

1)从程序内部重新编译。养成良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。

2)结束程序,内存自然就会被操作系统回收。

3)重新启动电脑后,立刻恢复。

  • new和malloc的区别

1、new分配内存按照数据类型进行分配,malloc分配内存按照指定的大小分配;

2、new返回的是指定对象的指针,而malloc返回的是void*,因此malloc的返回值一般都需要进行类型转化。

3、new不仅分配一段内存,而且会调用构造函数,malloc不会。

4、new分配的内存要用delete销毁,malloc要用free来销毁;delete销毁的时候会调用对象的析构函数,而free则不会。

5、new是一个操作符可以重载,malloc是一个库函数。

6、malloc分配的内存不够的时候,可以用realloc扩容。扩容的原理?new没用这样操作。

7、new如果分配失败了会抛出bad_malloc的异常,而malloc失败了会返回NULL。

8、申请数组时: new[]一次分配所有内存,多次调用构造函数,搭配使用delete[],delete[]多次调用析构函数,销毁数组中的每个对象。而malloc则只能sizeof(int) * n。

  • 共享内存相关api

  • reactor模型组成

  • 设计一下如何采用单线程的方式处理高并发

  • C++ STL 的内存优化

  • select,epoll的区别,原理,性能,限制都说一说

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值