在前面的文章 前言:编程综述 已经简单的介绍了,开发完的程序在其它电脑上可能遇见无法启动的问题,这里我们再次详细的讨论一下。不要小瞧这个简单问题,在其它机器无法运行程序只是这个问题的缩影之一。

1. Debug 和 Release

记得在学校学习C语言期间,老师在开课之前,拿出了一个控制台的万年历程序,并说如果谁可以写出这个程序,期末实践满分。

万年历

我将程序拷贝到自己的电脑研究,发现一个奇葩的问题,就是我写出的程序总是比老师的那个程序要大得多,即使是一个简单的 Hello World,一直到我学完C语言语法也没有搞明白是怎么回事,直到后来才晓得程序还有 Debug 和 Release 之分……

记得刚学习C语言写 Hello World 那会儿,了解C语言中加减乘除基本操作运算符之后,我一直都认为C语言也就能写个计算器而已……

这里不纠结程序 Debug 和 Release 具体的区别所在,只是想要读者知道程序开发的时候是分 Debug 和 Release 两种版本的。Debug 版本的程序会更大,因为其内部包含一些帮助开发者调试程序所需要的附带信息,而 Release 会优化程序,使程序更小更快,相应的程序也会更加难以调试。

一般在开发期间,都是使用 Debug 模式,这也是 Visual Studio 创建工程后的默认模式,具体可以在工具栏中查看:

vs-toolbar

这里除了 Debug 和 Release 可选外,还有 x86 和 x64 之分,请在开发期间确保下拉列表选中的是 Debug。

Windows 应用程序中,C语言的运行时库一般以动态库的形式链接进程序:

项目配置

注意看这个工程属性配置页面(选中左侧树形列表的工程,右键最后一项),左上角的 配置 属性为 活动(Debug),表示当前项目处于 Debug 状态,运行库默认是使用的 多线程调试DLL(/MDd),简单的说运行时库是以 .dll 加载进程序的。

还有一点需要注意,动态库也分为两种,一种是带调试信息的,一种不带,带调试信息的就是前面说的 Debug 版本。

如果你切换配置页面左上角的 配置 项为 Release,这时候你会发现工程属性中的 运行库 已经切换为 多线程DLL(/MD)

运行时库配置

多切换几次,观察左侧属性列表中每个选项卡的差别,百度一下二者不同选项的具体含义。

刚刚说到,程序以动态库的形式加载了运行时库,那如何能够查看确认呢?

如果你想确认当前程序运行起来加载了哪些动态库,最简单的方式就是先选中 Visual Studio,然后按 F5 将程序跑起来,在确保不关闭程序的情况下,回到 Visual Studio IDE 界面,查看下方 模块 列表:

vs 模块窗口

如果你在 IDE 中没有发现该窗口也不用着急,只需要去 调试 菜单下,打开即可:
调试菜单

注意看,列表中的第一项是我的 exe 程序,程序的名称就是工程名:first.exe。向下拉可以看到一个叫做 vcruntime140d.dll 的动态库:

vcruntime140d

这个动态库就是 Windows 平台的运行时库。这里请注意两点:

  • 这是一个 Debug 版本的动态库,因为动态库的名称后面包含了小写的 d 字母。

  • 这个动态库有版本号,这里是 140,这个版本是根据当前 Visual Studio 的版本来确定的,这里的 140 对应 Visual Studio 2015。

请关闭程序,切换 Release 版本,再次运行,查看和现在有什么不同。

2. Windows 程序中的运行时库

通过前面的学习,我们知道了运行时库分为 动态库静态库,并且还分为 调试(Debug) 版和 非调试(Release) 版本,最后运行时库还分为 不同的版本 ,每个版本都有不同的名称。

这里解释一下我的理解:

  • 之所以区分动态库和静态库是为了模块化,为了代码复用而考虑的,因为动态库可以被多个程序所共享,静态库则没有这个优势。

  • 之所以区分调试和非调试是为了代码可以方便寻找 bug,当程序出错的时候,你可以打印当前程序的运行堆栈,并可以找到发生错误位置的代码是哪个文件,哪一行等丰富的调试信息。

  • 之所以区分不同版本是因为 Visual Studio 每个版本都会有相应的改进和更新,如果不区分版本,则要考虑向前兼容的问题,因为你不知道当前电脑上的程序是否依赖于现有的运行库的版本。

了解了大致的原因后,我们简单的介绍一下,怎样确保程序可以在一个机器上顺利运行。

首先你要确保你程序所有依赖的资源文件、动态库、配置文件等等都已经放到了一起。例如游戏中播放的声音文件、程序中依赖的第三方动态库、程序自己的配置文件等等。

其次你要确保程序支持现有的 Windows 平台,有些程序 API 接口是平台独有的,未必向前兼容。

最后你要考虑的就是运行时库问题,常用的解决的方案有两种。一种是直接安装现有程序所以依赖的运行时库,具体可以到 MSDN 官方下载,也可以百度搜索 vc runtime,安装包大多都只有几M大小。另一种方案就是 前言:编程综述 中提到的,使用静态库,直接将程序使用的函数打包到程序中。

请注意你依赖的第三方动态库,很有可能你程序本身已经不需要运行时库,而第三方程序却需要,这时候程序一样无法运行。