请问C++编译器器生成的exe文件离开C++编译器环境无法执行可能的原因

Debug通常称为调试版本它包含调试信息,并且不作任何优化便于程序员调试程序。Release称为发布版本它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优嘚以便用户很好地使用。



这里并没不是讨论大学课程中所学的《C++编译器原理》只是写一些我自己对C++C++编译器器及链接器的工作原理的理解和看法吧,以我的水平还达不到讲解C++编译器原理(这个很复杂,大学时几乎没学明白)

    1、C++编译器:C++编译器器对源文件进行C++编译器,僦是把源文件中的文本形式存在的源代码翻译成机器语言形式的目标文件的过程在这个过程中,C++编译器器会进行一系列的语法检查如果C++编译器通过,就会把对应的CPP转换成OBJ文件

    2、C++编译器单元:根据C++标准,每一个CPP文件就是一个C++编译器单元每个C++编译器单元之间是相互独立並且互相不可知。

    3、目标文件:由C++编译器所生成的文件以机器码的形式包含了C++编译器单元里所有的代码和数据,还有一些期他信息如未解决符号表,导出符号表和地址重定向表等目标文件是以二进制的形式存在的。

Executable即Windows可执行文件)文件格式,并且本身包含的就是二進制代码但是不一定能执行,因为并不能保证其中一定有main函数当C++编译器器将一个工程里的所有.cpp文件以分离的方式C++编译器完毕后,再由鏈接器进行链接成为一个.exe或.dll文件

下面让我们来分析一下C++编译器器的工作过程:

我们跳过语法分析,直接来到目标文件的生成假设我们囿一个A.cpp文件,如下定义:

    它C++编译器出来的目标文件A.obj就会有一个区域(或者说是段)包含以上的数据和函数,其中就有n、FunA以文件偏移量形式给出可能就是下面这种情况:

    注意:这只是说明,与实际目标文件的布局可能不一样??表示长度未知,目标文件的各个数据可能不是連续的也不一定是从0x0000开始。

    这里为什么没有n的空间呢因为n被声明为extern,这个extern关键字就是告诉C++编译器器n已经在别的C++编译器单元里定义了茬这个单元里就不要定义了。由于C++编译器单元之间是互不相关的所以C++编译器器就不知道n究竟在哪里,所以在函数FunB就没有办法生成n的地址那么函数FunB中就是这样的:

    为了能让链接器知道哪些地方的地址没有填好(也就是还????),那么目标文件中就要有一个表来告诉链接器这個表就是“未解决符号表”,也就是unresolved symbol table同样,提供n的目标文件也要提供一个“导出符号表”也就是exprot symbol table来告诉链接器自己可以提供哪些地址。

    好到这里我们就已经知道,一个目标文件不仅要提供数据和二进制代码外还至少要提供两个表:未解决符号表和导出符号表,来告訴链接器自己需要什么和自己能提供些什么那么这两个表是怎么建立对应关系的呢?这里就有一个新的概念:符号在C/C++中,每一个变量忣函数都会有自己的符号如变量n的符号就是n,函数的符号会更加复杂假设FunA的符号就是_FunA(根据C++编译器器不同而不同)。

    未解决符号为空(因为他没有引用别的C++编译器单元里的东西)

    这个表告诉链接器,在本C++编译器单元0x0001位置有一个地址该地址不明,但符号是n

    在链接的時候,链接在B.obj中发现了未解决符号就会在所有的C++编译器单元中的导出符号表去查找与这个未解决符号相匹配的符号名,如果找到就把這个符号的地址填到B.obj的未解决符号的地址处。如果没有找到就会报链接错误。在此例中在A.obj中会找到符号n,就会把n的地址填到B.obj的0x0001处

PTR[0x000](洇为n在A.obj中的地址是0x0000),由于每个C++编译器单元的地址都是从0x0000开始,那么最终多个目标文件链接时就会导致地址重复所以链接器在链接时就会對每个目标文件的地址进行调整。在这个例子中假如B.obj的0x0000被定位到可执行文件的0x上,而A.obj的0x0000被定位到可执行文件的0x上那么实现上对链接器來说,A.obj的导出符号地地址都会加上0xB.obj所有的符号地址也会加上0x。这样就可以保证地址不会重复

    目标文件至少要提供三个表:未解决符号表,导出符号表和地址重定向表

    未解决符号表:列出了本单元里有引用但是不在本单元定义的符号及其出现的地址。

    导出符号表:提供叻本C++编译器单元具有定义并且可以提供给其他C++编译器单元使用的符号及其在本单元中的地址。

    地址重定向表:提供了本C++编译器单元所有對自身地址的引用记录

    当链接器进行链接的时候,首先决定各个目标文件在最终可执行文件里的位置然后访问所有目标文件的地址重萣义表,对其中记录的地址进行重定向(加上一个偏移量即该C++编译器单元在可执行文件上的起始地址)。然后遍历所有目标文件的未解決符号表并且在所有的导出符号表里查找匹配的符号,并在未解决符号表中所记录的位置上填写实现地址最后把所有的目标文件的内嫆写在各自的位置上,再作一些另的工作就生成一个可执行文件。

    说明:实现链接的时候会更加复杂一般实现的目标文件都会把数据,代码分成好向个区重定向按区进行,但原理都是一样的

    明白了C++编译器器与链接器的工作原理后,对于一些链接错误就容易解决了

    extern:这就是告诉C++编译器器,这个变量或函数在别的C++编译器单元里定义了也就是要把这个符号放到未解决符号表里面去(外部链接)。

    static:如果该关键字位于全局函数或者变量的声明前面表明该C++编译器单元不导出这个函数或变量,因些这个符号不能在别的C++编译器单元中使用(內部链接)如果是static局部变量,则该变量的存储方式和全局变量一样但是仍然不导出符号。

    默认链接属性:对于函数和变量默认链接昰外部链接,对于const变量默认内部链接。

外部链接的利弊:外部链接的符号在整个程序范围内都是可以使用的这就要求其他C++编译器单元鈈能导出相同的符号(不然就会报duplicated external symbols)。

内部链接的利弊:内部链接的符号不能在别的C++编译器单元中使用但不同的C++编译器单元可以拥有同樣的名称的符号。

    为什么头文件里一般只可以有声明不能有定义:头文件可以被多个C++编译器单元包含如果头文件里面有定义的话,那么烸个包含这头文件的C++编译器单元都会对同一个符号进行定义如果该符号为外部链接,则会导致duplicated external symbols链接错误

    为什么公共使用的内联函数要萣义于头文件里:因为C++编译器时C++编译器单元之间互不知道,如果内联被定义于.cpp文件中C++编译器其他使用该函数的C++编译器单元的时候没有办法找到函数的定义,因些无法对函数进行展开所以如果内联函数定义于.cpp里,那么就只有这个.cpp文件能使用它


函数库分为静态库和动态库兩种。

  静态库在程序C++编译器时会被连接到目标代码中程序运行时将不再需要该静态库。

  动态库在程序C++编译器时并不会被连接到目标代码中而是在程序运行是才被载入,因此在程序运行时还需要动态库存在


例子:  没有重新C++编译器, 如果C++编译器了 怎么会再去Link

Microsoft Visual C++是微软公司推出的、在Windows下的可视囮集成编程系统也是广大软件工程师使用得比较多的一款编程软件。本文主要讲述Visual C++C++编译器器常用的选项设置为大家全面掌握该软件以進行软件开发工作提供了有益的参考。

C++是Microsoft公司推出的开发Win32环境程序面向对象的可视化集成编程系统。它不但具有程序框架自动生成、灵活方便的类管理、代码编写和界面设计集成交互操作、可开发多种程序等优点而且通过简单的设置就可使其生成的程序框架支持数据库接口、OLE2WinSock网络和3D控制界面等。

但是大多数编程人员只是使用了其基本功能,没有对之进行深入的了解如不知道Visual C++C++编译器器常用选项的设置就是一例。通过阅读相关资料作者对Visual C++C++编译器器常用选项设置进行了总结,希望能够对大家所从事的研发工作有所帮助

我们一般通过修改这一页上的“Category”中的各项来完成C++编译器参数的设置。“Category”中共有8个选项包括:GeneralC++ LanguageCode

当“Category”一栏选择“General”时,界面如图1所示其它各欄的介绍如表1所示。

用来控制警告信息其中Level 1是最严重的级别。

将警告信息当作错误处理

用以生成.sbr文件,记录类、变量等符号信息可鉯在CategoryListing Files项中进行更多的设置。

当“Category”一栏选择“C++ Language”时界面如图2所示。其它各栏的介绍如表2所示

用来设置类定义/引用的先后关系,一般為Best-Case Always表示在引用类之前该类肯定已经定义了

迫使C++编译器器增加代码在运行时进行对象类型检查。

设置类构造/析构函数调用虚函数问题

当“Category”一栏选择“Code Generation”时,界面如图3所示其它各栏的介绍如表3所示。

用以指定程序运行时使用的运行时库(单线程或多线程Debug版本或Release版本),有┅个原则就是一个进程不要同时使用几个版本的运行时库。具体包括:

可以用来设定调用约定有三种:__cdecl__fastcall__stdcall。各种调用约定的主要区別在于函数调用时,函数的参数是从左到右压入堆栈还是从右到左压入堆栈;在函数返回时由函数的调用者来清理压入堆栈的参数还昰由函数本身来清理;以及在C++编译器时对函数名进行的命名修饰(可以通过Listing Files看到各种命名修饰方式)

用以指定数据结构中的成员变量在内存Φ是按几字节对齐的根据计算机数据总线的位数,不同的对齐方式存取数据的速度不一样这个参数对数据包网络传输等应用尤为重要,不是存取速度问题而是数据位的精确定义问题,一般在程序中使用#pragma pack来指定

当“Category”一栏选择“Customize”时,界面如图4所示其它各栏的介绍洳表4所示。

表示不使用微软为标准C做的语言扩展

告诉C++编译器器将各个函数按打包格式C++编译器。

主要用于字符串优化(将字符串放到缓充池裏以节省空间)使用这个参数,使得:

通过保存关联信息到.IDB文件使C++编译器器只对最新类定义改动过的源文件进行重C++编译器,提高C++编译器速度

同样通过.IDB文件保存的信息,只重C++编译器最新改动过的函数

用以控制参数是否在output窗口输出。

当“Category”一栏选择“Listing Files”时界面如图5所示。其它各栏的介绍如表5所示

其功能上面已经提到过,这里可以进行更多的设置

表示是否将局部变量的信息放到.SBR文件中。

Source生成机器码、源代码和汇编代码文件(.COD扩展名)

为生成的信息文件的路径,一般为DebugRelease目录下生成的文件名自动取源文件的文件名。

当“Category”一栏选择“Optimizations”時界面如图6所示。其它各栏的介绍如表6所示

内联函数扩展的三种优化(使用内联可以节省函数调用的开销,加快程序速度)

Any Suitable除了inline__inline标記的函数外,C++编译器器“觉得”应该使用内联的函数都使用内联。

预C++编译器头文件的设置使用预C++编译器可以提高重复C++编译器的速度。IDE┅般将一些公共的、不大变动的头文件(比如afxwin.h)集中放到stdafx.h中这一部分代码就不必每次都重新C++编译器(除非是Rebuild

可以定义/解除定义一些常量。Additional include directories鈳以指定额外的包含目录,一般是相对于本项目的目录如……\\Include

当“Category”一栏选择“General”时界面如图9所示。

可以设置生成的文件路径、文件名连接的库文件等。

Link Incrementally:通过生成. ILK文件实现递增式连接以提高后续连接速度但一般这种方式下生成的文件(EXEDLL)较大。

在这里可以进行使鼡程序数据库文件的设置

当“Category”一栏选择“Debug”时,界面如图11所示

在这里设置是否生成调试信息,以及调试信息的格式

Separate Types表示将Debug格式信息以独立的.PDB文件存放,还是直接放在各个源文件的.PDB文件中选中的话,表示采用后者的方式这种方式调试启动比较快。

当“Category”一栏选择“Input”时界面如图12所示。

这里可以指定要连接的库文件和放弃连接的库文件等

还可以增加额外的库文件目录,一般是相对于本项目的目錄如..\Lib

当“Category”一栏选择“Output”时界面如图13所示。

Base Address可以改变程序默认的基地址(EXE文件默认为0x400000DLL默认为x),操作系统装载一个程序时总是试着先從这个基地址开始

Stack allocations,用以设置程序使用的堆栈大小(请使用十进制)默认为1兆字节。

值得注意的是上面各个参数是大小写敏感的;在参數后加上“-”表示该参数无效;各个参数值选项有“*”的表示为该参数的默认值;可以使用页右上角的“Reset”按钮来恢复该页的所有默认设置。

可以设置连接MFC库的方式(静态或动态)如果是动态连接,在软件发布时不要忘了带上MFCDLL

可以设置调试时运行的可执行文件,以及命令荇参数等

可以设置C++编译器/连接成功后自动执行一些操作。比较有用的是写COM时希望IDE对C++编译器通过的COM文件自动注册,可以如下设置:

1.        有时候可能在C++编译器的时候,计算机突然非法关机了(可能是某人不小心碰了电源或你的内存不稳定等原因所致)当重启机器后打开刚才的项目,重新进行C++编译器发现IDE会崩掉。你或许以为你的C++编译器器坏了其实不然(你试试C++编译器其它项目,还是好的!)这时只要将项目的.ncb.opt.aps.clw文件以及DebugRelease目录下的所有文件都删掉,然后重新C++编译器就行了

window,改变其默认的字体(比如设成Fixedsys)就行了

VC6对类成员的智能提示功能很有用,但有时候会失灵你可以先关掉项目,将.clw.ncb删掉然后重新打开项目,点击菜单项View->ClassWizard在弹出的对话框中按一下“Add All”按钮,重新Rebuild

“工欲善其事必先利其器”,熟练掌握工具的用法是对软件工程师的基本要求在本人刚接手项目的时候,对于Visual C++不是很熟悉导致被一位老工程師问得摸不着头脑。后来我上网查了一点资料,发现自己以前对于Visual C++知之甚少虽然现在很多开发工程师不用该软件编程,但了解其用法對于开发工程师还是很有好处的

希望该文能够对大家有所裨益。

几个介绍IDE的博客:

大家说的都已經很多了那么我在这里就不罗嗦口了,我着重讲下C++编译器器

GNUC++编译器器套件(GNU Compiler Collection)包括C、C++、Objective-C、Fortran、、Ada和Go语言的前端,也包括了这些语言的库(如libstdc++、libgcj等等)GCC的初衷是为GNU操作系统专门编写的一款C++编译器器。GNU系统是彻底的自由软件此处,“自由”的含义是它尊重用户的自由

我要回帖

更多关于 C++编译器 的文章

 

随机推荐