{
在编译过程中,编译器会将我们的代码编译成汇编文件,这里其实就存在这一种重命名机制,我们把它就叫做名字修饰。
名字修饰内容
1、在C语言中,编译器在编译过程会将我们的函数重命名,具体的方法就是在我们的函数名前加上“_“修饰符,通过这种方式就可以在我们的符号表种查找到了,但是假如有两个相同的函数,编译之后进行相同的重命名,在符号表中生成的函数名一样,那么就无法区分到底是哪个函数了,所以这也就是我们的C语言为什么不支持函数重载的原因了。
2、在C++中,既然支持函数重载,那么它肯定对C语言在这方面进行了优化,具体的方法就是在我们的函数名后面加上参数然后生成我们符号表中的函数名称。那么这样一来,就很容易理解了,为什么C++可以支持函数重载了,就是因为函数重载底层的原理造成的,就是由于重载函数在符号表中生成的函数名称不一样,这样就能区分到底是哪个函数了。这样一来编译就能通过了。
}
{
一般继承(无虚函数覆盖)
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
一般继承(有虚函数覆盖)
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。
多重继承(无虚函数覆盖)
1)每个父类都有自己的虚表。
2)子类的成员函数被放到了第一个父类的表中。(所谓的第一个父类是按照声明顺序来判断的)
多重继承(有虚函数覆盖)
VC中查看虚函数表
我们可以在VC的IDE环境中的Debug状态下展开类的实例就可以看到虚函数表了(并不是很完整的)
}
{
虚函数表:由类维护,而类的每一份对象中有一个指针,指向着这个虚函数表。虚函数表是顺序存放虚函数地址的,虚表是顺序表,表里存放了虚函数的地址。
虚指针(vptr):每个含有虚方法(虚函数)对象里有虚表指针,指向虚表。
}
{
C中,内存分为5个区:堆(malloc)、栈(如局部变量、函数参数)、程序代码区(存放二进制代码)、全局/静态存储区(全局变量、static变量)和常量存储区(常量)。此外,C++中有自由存储区(new)一说。
全局变量、static变量会初始化为缺省值,而堆和栈上的变量是随机的,不确定的。
}
{
1).堆存放动态分配的对象——即那些在程序运行时动态分配的对象,比如 new 出来的对象,其生存期由程序控制;
2).栈用来保存定义在函数内的非static对象,如局部变量,仅在其定义的程序块运行时才存在;
3).静态内存用来保存static对象,类static数据成员以及定义在任何函数外部的变量,static对象在使用之前分配,程序结束时销毁;
4).栈和静态内存的对象由编译器自动创建和销毁。
}
{
程序编译的过程中就是将用户的文本形式的源代码(c/c++)转化成计算机可以直接执行的机器代码的过程。主要经过四个过程:预处理、编译、汇编和链接。
预处理:生成.i文件; g++ -E hello.cpp > hello.i
{
主要功能:
展开所有的宏定义,消除“#define”;
处理所有的预编译指令,比如#if、#ifdef等;
处理#include预编译指令,将包含文件插入到该预编译的位置;
删除所有的注释“/**/”、"//"等;
添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及错误提醒;
保留所有的#program编译指令,原因是编译器要使用它们;
}
编译:生成.s汇编文件(汇编指令);g++ -S hello.cpp
{
C++程序的编译过程是分模块进行的,每一个模块独立编译,生成相应的.obj或.o文件,一般情况下,每一个.c或.cpp文件作为一个独立的模块进行编译。
.h文件是不会被编译的。
这个编译过程会检查变量和函数是否有被声明,进行词法检查,语法检查、、、,生成汇编代码,优化汇编代码,最后经过汇编程序翻译成目标机器代码,生成.obj或.o文件。
}
汇编:汇编变为目标代码(机器代码)生成.o的文件(二进制机器码);g++ -c hello.cpp
链接:(在链接阶段就是要把我们的所有的目标文件以及我们所依赖的库文件链接到一起生成可执行程序)
函数库(可以这么理解就是不带main()函数的.cpp生成的)。
{
因为每一个模块是独立编译的,所以对于定义在其它模块的函数或变量的使用都是未定义的,编译时检查到有声明只是告诉模块--这个函数或变量定义在其它模块中了,当然这样编译后生成的文件是无法直接执行的。因此,我们需要一个过程将各个编译后的模块合理组合起来,并将各个模块中对其它模块的函数调用或变量引用设置到正确的地址,这个过程就叫做链接。链接完各个.obj或.o文件后才能生成一个可执行文件。
链接过程需要编译时收集每一个模块的相关信息才能完成,编译每一个模块的同时还要生成三个表供链接过程使用:
(1 )导出符号表:提供了本编译单元具有定义,并且愿意提供给其他编译单元使用的符号及其地址。
(2)未解决符号表:提供了所有在该编译单元里引用但是定义并不在本编译单元里的符号及其出现的地址。
(3)地址重定向表:因为每个模块的逻辑地址都是从0开始的,这样的地址是相对的,在链接成可执行文件之前需要知道每个.obj文件在可执行文件中的起始位置,这样(相应.obj的起始位置+导出符号表中导出符号对应的位置)才是一个正确的的地址。这样,我们就需要告诉链接器,哪些地址是需要重定位的。这样,链接器在进行链接时,首先会决定每一个.obj文件在可执行文件中的起始位置,然后查看模块的地址重定向表,在需要重定向的位置上加上相应模块实际在可执行文件中的起始位置,这样的地址才是正确的地址。
***
对于每个模块,一般而言:
(1)用extern关键字修饰的符号是外部链接,它告诉编译器,这个符号定义在其它模块,应该把这个符号放入未解决符号表。
(2)用static关键字修饰的符号都是内部链接符号,也就是说这些符号仅仅在模块内部可见,不会提供给其它模块引用,也就不会被放进导出符号表。
(3)默认情况下,const常量是内部链接,不会被加到导出符号表中。
(4 ) 默认情况下,函数和全局变量都是外部链接符号,这些符号会被放入模块的导出符号表,以供其它模块使用,
但是可以用static关键字修饰,把它变成内部链接,这样就不会被放进导出符号表。
链接过程
(1)决定每一个obj(模块)在可执行文件中的位置。
(2)查看每一个模块的重定向表,给需要重定向的值加上它所在模块在可执行文件中的起始位置,形成正确的地址。
(3)检查所有模块的导出符号表,如果发现导出符号有重复,会产生链接错误: duplicated external simbols...,然后停止链接过程。
(4)在导出符号表中搜索未解决符号表中的符号,找到后会在相应位置填上正确地址,如果找不到,就会产生链接错误: unresolved external link...,然后停止链接过程。
(5)完成链接过程,生成可执行文件。
}
}
{