C/C++ 编译链接与内存分配情况
C/C++ 语言的编译链接过程
源代码-->预处理-->编译-->优化-->汇编-->链接-->可执行文件
1) 预处理
读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。包括宏定义替换、条件编译指令、头文件包含指令、特殊符号。 预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。.i预处理后的c文件,.ii预处理后的C++文件。
2) 编译阶段
编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。.s文件
3) 汇编过程
汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。.o目标文件
4) 链接阶段
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
C/C++程序编译的内存分配情况
一个C、C++程序编译时内存分为5大存储区:堆区、栈区、全局区、文字常量区(常量字符串就是放在这里的。 程序结束后由系统释放)、程序代码区。
静态存储区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static变量,常量字符串等。
栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。大小为2M。
堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由OS(操作系统)回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。即动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
程序代码区 —存放函数体的二进制代码。
内链接与外链接
编译器在编译代码时,只会去编译.cpp格式的源文件,并且预编译器会递归的把.cpp所有#include的头文件都“拷贝”到.cpp文件中去,之后对这个文件再进行编译,生成二进制的.obj文件。那么其实每一个.cpp文件都是一个编译单元。
内链接:
如果一个名称对于他的编译单元是局部的,并且在链接时不会与其他的编译单元中有同样的名字冲突,那么这个名称就拥有内部链接。这个实体有内部链接,他就不会与其他.cpp文件同名的实体冲突。换个说法,那些编译单元(.cpp)中不能向其他编译单元(.cpp)展示提供其定义的函数、变量就拥有内部链接。
哪些实体拥有内部链接?
1.静态(static)全局变量,自由函数,友元函数定义
2.类的定义
3.内联函数定义
4. Union共用体定义
5.名字空间的const常量定义
6.枚举类型定义
7.所有的声明(有人将声明归结为无链接)
外链接:
一个多文件的程序中,一个实体可以在链接时与其他编译单元交互,那么这个实体就拥有外部链接。
换个说法,那些编译单元(.cpp)中能向其他编译单元(.cpp)提供其定义,让其他编译单元(.cpp)使用的函数、变量就拥有外部链接。
那么哪些实体拥有外部链接?
1.类的非内联函数(包括成员函数和静态类成员函数)的定义
2.类的静态成员变量的定义
3.名字空间或全局的非静态的自由函数,非静态变量,非友元函数的定义
C++ 对于同时存在内部和外部同名符号时,只认内部符号。
内联函数也无法参与外链接
可以理解为外链接就是要让连接器去其他的obj中查找符号的引用行为。内链接则是不需要链接器去其他obj查找的引用行为。
会发生外链接的行为(涉及到类之后,会有更多情况,这里只是一部分)
-
引用了其他cpp中定义的全局变量。
-
引用了其他cpp中定义的 非静态,非内联函数。
仅内链接的行为
- 静态的变量
- 静态函数
- 内联函数