VS C++工程的编译和链接

cl.exe和link.exe分别是visual studio 中的编译器和链接器

配置属性中的【c/c++】(设置编译的一些选项) 和 【链接器】选项页中,最后的那个命令行汇总了所有生效的设置,就是最终执行的命令行

配置属性中的VC++ 目录用于设置各个路径,相当于设置环境变量PATH(搜可执行文件的路径):

INCLUDE(搜include中文件的路径)

LIBPATH(搜使用using 引入文件的路径)

LIB(搜库文件的路径)

编译过程:语法错误

链接过程:结构错误

运行过程:逻辑错误

目录

编译:

1.头文件路径设置

2.优化选项设置

3.汇编输出文件设置

4.obj文件查看

1.使用/summary选项,或者不输入任何选项

2.使用/headers 选项

 3.使用/section:段名 查看具体的段的内容

4.使用/symbols 查看符号表

5.运行库设置

链接:

1.一些链接错误

2.附加依赖库

3.符号解析过程

可执行文件


编译:

一般一个大的项目,修改完一个cpp以后,一般都先单独编译一下这个cpp,免得到整个工程生成的时候发现这个cpp的一些编译错误。把光标停留在某个cpp的页面中,然后【生成】--【编译】,或直接ctrl+F7

编译所做的事情:

对一个cpp进行预处理,将头文件加载进来,并且将各种#define信息代入,生成一个独立的编译单元,然后进行编译生成obj文件,一个cpp对应一个obj文件。

编译过程,先生成汇编语言,再生成机器语言,即汇编指令对应的机器码。

1.常规设置

1)头文件路径设置

除了cpp定义的include,还有属性中可以配置的附加包含目录,以及VC++目录中的包含目录。

ps:【VC++目录中的包含目录】和【C/C++常规中的附加包含目录】的区别:

包含目录:修改了系统的include宏的值,是全局的;

附加包含目录:用于当前项目,对其他项目没有影响。

(同理库目录附加库目录的区别)

2)调试信息格式

  • (没有)
    • 不创建调试信息
    • 编译时间更快
  • / Z7
    • 使用CodeView格式在.obj文件中生成完整的符号调试信息
  • /Zi
    • 使用程序数据库格式在目标的.pdb文件中生成完整的符号调试信息。
    • 支持最小重建(/ Gm),这可以减少重新编译所需的时间。
  • / ZI
    • 除了支持Edit-and-Continue之外,生成像/ Zi这样的调试信息

2.优化选项设置

在不同的配置中,release 和 debug,默认的优化选项也不一样。

优化选项不一样,最后生成的机器码也不一样。

根据需要可以进行自行配置

选择启用优化的话,针对一些没有用的代码,就会被优化掉,也就是目标文件中就不会有对应的机器码了。这里有介绍过应用。

3.汇编输出文件设置

选择汇编程序输出,默认选择的是 无列表【C/C++-》输出文件-》汇编输出程序】

其他值的含义:

/FA      仅输出汇编到文件, 文件默认扩展名是 .asm。

/FAc    输出汇编和相应的机器码到文件,文件默认扩展名是 .cod。

/FAs    输出汇编和相应的源代码到文件,文件默认扩展名是 .asm。

/FAcs     输出汇编机器码源代码到文件,文件默认扩展名是 .cod。

一般如要选的话,就选择最后一个FAcs,可以通过这个辅助查找崩溃的具体行。具体可以查看https://blog.csdn.net/wind19/article/details/40614745

4.obj文件查看

可以使用dumpbin.exe 查看obj文件

dumpbin是在Windows平台下用于显示COFF格式文件信息的一个命令行工具。你可以使用DUMPBIN去显示COFF格式的文件信息,比如像vc编译器生成的目标文件(obj),可执行文件(exe)和动态链接库(DLLs)等。

1.使用/summary选项,或者不输入任何选项

显示:每个段的基本信息(大小+段名)。

这里.obj 是 COFF OBJECT

另外还有:

.lib 是 LIBRARY

.dll 是 DLL

.exe 是 EXECUTABLE IMAGE

2.使用/headers 选项

所有节(SECTION)的描述结构,即节头

先显示一个汇总,

再分别显示各个section, SECTION HEADER #1 到 SECTION HEADER #B,11个

最后再显示上面1中显示的summary

 3.使用/section:段名 查看具体的段的内容

/summary 中显示出来的段名

bss段存放的是未初始化的全局变量或(全局和局部的)静态变量。

data段存放的是初始化的全局变量或(全局和局部的)静态变量。

显示各个段的一些信息

:预留空间,cpp中分别定义了一个初始化的和未初始化的全局变量int,分别在data段和bss段,均占用4个字节。

:读写权限,bss和data段都是可读可写的段

:字节对齐align,影响预留空间 size of raw data,如果int int char 就是9,如果是int char int 就是12。

关于const变量属于只读。

const全局变量存储在只读数据段,编译期最初将其保存在符号表中。当运行时第一次使用时为其分配内存,在程序结束时释放。

const局部变量存储在栈中,栈区也是运行时才有的。当运行时第一次使用时为其分配内存,代码块结束时释放。

vs

只有存在于data段全局变量和静态变量,在编译期就分配空间。(bss段编译器不分配空间的file pointer to raw data 是 空,bss段只是有个占位符,运行的时候系统自动都初始化为0

4.使用/symbols 查看符号表

使用/symbols 查看符号表,符号就是通常指定义出的函数,全局变量。

源文件的全局符号 (global symbol) 分成强 (strong) 和弱 (weak) 两类传给汇编器

汇编器则将强弱信息编码并保存在目标文件的符号表中

编译器认为函数初始化了的全局变量都是强符号,而未初始化的全局变量则成了弱符号

5.运行库设置

设置在【配置属性-》C/C++->代码生成-》运行库】

C和C++运行时库  由编译器分别实现,实现的内容是C标准和C++标准定义了一系列常用的函数(标准只是定义函数原型,编译器来实现),实现的库分别称为CRT库C++类库

比如 main() exit() 就是CRT 提供c程序运行的基本函数。

VC2010使用的CRT库的DLL版本在MSVCR100.DLL中实现,对应调试版本为MSVCR100D.DLL

VC2010使用的CRT库的LIB版本在libcmt.lib中实现,对应的调试版为libcmtd.lib

VC2010使用的C++类库DLL版本在MSVCP100.DLL中实现,对应调试版本为MSVCP100D.DLL

VC2010使用的C++类库LIB版本在libcpmt.lib中实现,对应的调试版为libcpmtd.lib

由于C++对C的兼容性,C++标准库包括了C标准库,除此之外还包括IO流和标准模板库STL

(用Dependency Walker打开MSVCRT100.DLL,我们可以在其中找到我们经常使用使用的C函数,如printf ,getchar,malloc等。打开MSVCP100.DLL,也可以找到这些C函数。)

【配置属性-》C/C++->代码生成-》运行库】中的不同选项就是选择不同的DLL版本还是LIB版本,调试版本还是发布版本:

MT选项:LIB版的C和C++运行库。在链接时就会在将C和C++运行时库集成到程序中成为程序中的代码,程序体积会变大。

MTd选项:LIB的调试版。

MD选项:DLL版的C和C++运行库,这样在程序运行时会动态的加载对应的DLL,程序体积会减小,缺点是程序在系统没有对应DLL时程序无法运行。

MDd选项:DLL的调试版。


6.编译错误

1) C1189 #error:  /RTCc rejects conformant code, so it is not supported by the C++ Standard Library. Either remove this compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have received this warning. 

代码生成-》较小类型检查 从/RTCc 改成 否 

2)error C2039: “unique_ptr”: 不是“std”的成员

因为没有包含 unique_ptr 所在的头文件 #include<memory>
    

3)c1xx : fatal error C1083: 无法打开源文件:“lllll”: No such file or directory

看看是不是真的无法打开 lllll 没有找到这个源文件
  

4)无法打开预编译头文件:“xxx.pch”: No such file or directory 

将 C++->预编译头 【创建/使用编译头】 改为“不使用编译头”

5)命令行编译msbuild 提示找不到头文件

在jenkins上使用 msbuild命令行编译工程的时候 提示找不到某头文件,但打开vs工程编译没有问题。

原因是在工程的配置中有添加头文件目录,但是 msbuild 不认识$(SolutionDir)这个宏,所以提示找不到头文件

解决加上一个参数,定义此宏:

/p:SolutionDir=path

https://stackoverflow.com/questions/15053915/how-to-get-rid-of-solutiondir-when-building-visual-studio-project-from-outs

解决方法二:使用硬链接,放到系统的include下

cd /d C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include
mklink /D 取个文件夹名 头文件真正所在的路径

6) msbuild和devenv的区别 还没搞明白

在jenkins上编译工程的时候,同一个sln下,有的用msbuild 有的用devenv。

set MSBuild=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
set devenv="C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe"
set Src=%JENKINS_HOME%\jobs\Checkout\workspace\XXX\Src
set PubSrc=%JENKINS_HOME%\jobs\Checkout\workspace\YYY\Project
set Config=/t:Rebuild /p:Configuration=Release;Platform=Win32
set DevConfig=/Rebuild "Release|Win32"

cd /d %Src%\UnrealBuildTool\
::Msbuild 
%MSBuild% UnrealBuildTool.csproj %Config%;OutputPath=..\..\Intermediate\UnrealBuildTool\Release\
%MSBuild% %PubSrc%\Common.vcxproj %Config%
::devenv
%devenv% %Src%\GamePhysLibDev.vcxproj %DevConfig%

链接:

链接器 (linker) 将一个个的目标文件 ( 还会有若干程序库 ) 链接在一起生成一个完整的可执行文件。

1.一些链接错误

我们经常在遇到一些连接错误

1.LNK1104 ,这里遇到过一次

2.LNK2005 ,重定义错误,可能跟不同项目使用了不同的运行库有关系(上述的运行库设置)

3.LNK1169 

在链接的时候有三个规则:

规则 1: 不允许强符号被多次定义 ( 即不同的目标文件中不能有同名的强符号 ) ;

规则 2: 如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;

规则 3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;

2.附加依赖库

或者 #pragma comment(lib, “**.lib”)

3.符号解析过程

在符号解析 (symbol resolution) 阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序(配置属性-链接器-命令行)从左至右把他们放入输入文件列表中,然后依次扫描它们,在此期间它要维护若干个集合 :

(1) 集合 E 是将被合并到一起组成可执行文件的所有目标文件集合;

(2) 集合 U 是未解析符号 (unresolvedsymbols ,比如已经被引用但是还未被定义的符号 ) 的集合;

(3) 集合 D 是所有之前已被加入到 E 的目标文件定义的符号集合。

一开始,这三个集合都是空的。 链接器的工作过程:

(1) 对命令行中的每一个输入文件 f ,链接器确定它是目标文件还是库文件,如果它是目标文件,就把 f 加入到 E ,并把 f 中未解析的符号和已定义的符号分别加入到 U 、 D 集合中,然后处理下一个输入文件。

(2) 如果 f 是一个库文件,链接器会尝试把 U 中的所有未解析符号与 f 中各目标模块定义的符号进行匹配。如果某个目标模块 m 定义了一个 U 中的未解析符号,那么就把 m 加入到 E 中,并把 m 中未解析的符号和已定义的符号分别加入到 U 、 D 集合中。不断地对 f 中的所有目标模块重复这个过程直至到达一个不动点 (fixed point) ,此时 U 和 D 不再变化。而那些未加入到 E 中的f 里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。

(3) 如果处理过程中往 D 加入一个已存在的符号,或者当扫描完所有输入文件时 U 非空,链接器报错并停止动作。否则,它把 E 中的所有目标文件合并在一起生成可执行文件。

可执行文件

编译链接生成的可执行文件

1.可以在操作系统中直接运行

2.包含:1)机器码和数据 2)相关的描述信息,比如程序有多大,要占多少内存空间

操作系统根据描述信息,讲可执行文件中的机器码和数据装入内存,并进行相关的初始化操作,比如将设置cs:ip指向第一条要执行的指令。

是谁将可执行文件中程序装载进入内存并使它运行

DOS系统中是通过command.com 命令解释器将可执行文件装入内存中。

command设置cpu的cs:ip指向程序的第一条命令,将cpu的控制权交给他,然后程序退出以后,控制器交回给command

debug可以将程序加载入内存,但是不放弃对cpu的控制,这样就可以利用debug的相关命令来单步执行程序。

发布了104 篇原创文章 · 获赞 44 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/u012138730/article/details/90749833