说明
当我们在Windows下开始学习编程的时候,通常会用到Visual Studio这个工具进行代码开发。
通常我们会打开Visual Studio(这里以Visual Studio Community 2015为例,因此Community版本是免费的),然后在下面创建项目,写代码,并进行编译和执行。下面是一个例子(创建项目的过程略):
在上述的界面选择"生成->生成解决方案"进行编译,然后选择"调试->开始执行(不调试)"就可以运行程序,结果如下:
通常到这里之后,我们会觉得在编程的道路上跨出了一大步,但是实际上呢,感觉什么也没有做。
这是因为Visual Studio为我们完成了几乎所有的操作,我们只是写了几行字,点了几个按键而已。
这对编程的学习并不是很有好。
所以通常作为初学者会被告知,要更好地学习编程,可以考虑在Linux下进行,从中我们可以学到预编译,编译,链接等种种操作,从而对软件开发有更深入的了解。
这当然是好的,但是为什么要在Linux下呢?实际上Visual Studio本身也提供了种种的工具,我们可以拿这些工具,自己进行编译相关的种种操作。
本文的目的就是介绍和使用这些工具来进行代码开发。
工具在哪里
首先我们当然需要找到Visual Studio提供的工具在哪里,由于Visual Studio隐藏了种种的细节,并没有给我们看工具在哪里(也有可能是Visual Studio提供的IDE界面太复杂了导致我没有找到),所以需要我们自己在安装目录中查找。
下面是具体的位置(还是以Visual Studio Community 2015为例):
上述的这些exe文件就是Visual Studio提供的编译相关工具。
比如cl.exe就是Visual Studio提供的C/C++编译器:
下面就是介绍如何使用这些工具来进行代码开发。
一个有点复杂和啰嗦的例子
首先我们创建一个目录,在里面存放相关的代码:
先来一个HelloWorld.c:
#include <stdio.h>
int main() {
printf ("Hello world\n");
return 0;
}
然后我们在当前目录下打开一个CMD窗口,并调用cl.exe文件:
先不管cl.exe的使用方式,这里的首要问题是找不到cl.exe。
当然我们是知道cl.exe在哪里的,前面已经讲过;但是系统并不知道,为了让系统知道,需要将cl.exe对应的路径加入到环境变量中,这个也不难,如下:
之后重新打开一个CMD窗口就可以找到:
之后再执行上述的操作:
结果还是报错了,原因在于我们的代码中包含了一个头文件<stdio.h>,而目前cl.exe并不知道去哪里找这个头文件。
那么这个头文件到底在哪里呢?
这似乎也不是什么难的问题,盲猜还是在Visual Studio安装目录下面,结果猜错了......实际上也跟Visual Studio有关,因为这个Windows Kits也是在安装Visual Studio的时候安装的(所以安装的时候需要注意选项):
不过问题来了,如何在执行cl.exe的时候指定相应的文件呢?
查看cl.exe的帮助(cl.exe /?),我们发现cl.exe下包含中断的参数,而其中恰好有跟头文件相关的内容:
那么就添加这个参数试试吧(注意需要引号将路径引用起来,否则会报错):
可以看到似乎有效,但是还是有问题,就是需要包含额外的头文件,找到这个文件再放到参数中:
看来头文件的问题解决了,但是还是有其它的问题,这个问题就在于printf()函数并没有在我们的代码中实现,所以必须要使用库函数,这需要通过链接来实现,这也是为什么这里打印错误的是LINK这一步。
那么如果链接到LIBCMT.lib这个库呢?
这个时候就需要看link.exe的帮助说明了:
似乎可以直接加上lib库就行:
这个时候发现又少其它lib,继续找,知道找到所有:
终于找到了,并成功生成了exe文件。
这当然太复杂了,下面使用了link.exe的一个参数/LIBPATH:
/LIBPATH指定目录而不是直接指定lib库,这让情况稍微简单了一点,但是还是太麻烦了。
有没有更简单的办法吗?
一种更简单的方法
当我们在安装Visual Studio的时候,会创建如下的一个系统变量(注意还是以Visual Studio Community 2015为例):
通过它我们就能够知道我们的系统安装了某个Visual Studio版本,并得到对应的bin目录:
因为我们的系统是x64的,所以这里指定64位的编译,所以对应到这里的目录是x86_amd64。
在这个目录下可以找到红框内的批处理文件,而执行它就可以直接为我们配置好一般环境,包括PATH,LIBPATH等,这也就不需要我们手动去指定。这里写了一个批处理:
@echo off
:: This is for Visual Stdio 2015.
if defined VS140COMNTOOLS (
:: Make sure the Visual Stduio is default installed, or the path will be wrong.
set "COMMONTOOLSx64=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64"
goto VsFound
)
:: No Visual Studio is intalled on this system.
echo.
echo Visual Studio not found.
echo.
exit /B 1
:VsFound
:: Visual Studio is found, just call the bat in the the directory of %COMMONTOOLSx64%.
if exist "%COMMONTOOLSx64%\vcvarsx86_amd64.bat" (
@call "%COMMONTOOLSx64%\vcvarsx86_amd64.bat"
@if errorlevel 1 (
@echo. ERROR setting Microsoft Visual Studio %1
@set COMMONTOOLSx64=
@exit /B 1
)
)
@echo on
:: Open a prompt where Visual Studio tools can be used.
@cmd
这样只需要点击个脚本,就可以完成需要的配置,并进入到编译的状态,点击上述脚本之后的结果如下:
显然这要比之前的一大堆操作简单很多。
当然这里的实现依赖于一点,即HelloWorld.c中调用的接口是Visual Studio本身就提供的,如果是其它的库,还是需要自己添加库,当然也可以修改上述的脚本来添加库和头文件,这样会更方便一些。
vcvarsx86_amd64.bat做了什么
为什么在执行了vcvarsx86_amd64.bat之后就可以直接使用cl.exe了呢?
原因可以在这个脚本中找到,实际上它就是设置了一堆系统变量,比如PATH,INCLUDE,LIB,LIBPATH等等:
@rem INCLUDE
@rem -------
@if exist "%VCINSTALLDIR%ATLMFC\INCLUDE" set INCLUDE=%VCINSTALLDIR%ATLMFC\INCLUDE;%INCLUDE%
@if exist "%VCINSTALLDIR%INCLUDE" set INCLUDE=%VCINSTALLDIR%INCLUDE;%INCLUDE%
@rem LIB
@rem ---
@if "%1" == "store" goto setstorelib
@if exist "%VCINSTALLDIR%ATLMFC\LIB\amd64" set LIB=%VCINSTALLDIR%ATLMFC\LIB\amd64;%LIB%
@if exist "%VCINSTALLDIR%LIB\amd64" set LIB=%VCINSTALLDIR%LIB\amd64;%LIB%
@goto setlibpath
:setstorelib
@if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB%
:setlibpath
@rem LIBPATH
@rem -------
@if "%1" == "store" goto setstorelibpath
@if exist "%VCINSTALLDIR%ATLMFC\LIB\amd64" set LIBPATH=%VCINSTALLDIR%ATLMFC\LIB\amd64;%LIBPATH%
@if exist "%VCINSTALLDIR%LIB\amd64" set LIBPATH=%VCINSTALLDIR%LIB\amd64;%LIBPATH%
@goto appendlibpath
:setstorelibpath
@if exist "%VCINSTALLDIR%LIB\store\amd64" set LIBPATH=%VCINSTALLDIR%LIB\store\amd64;%VCINSTALLDIR%LIB\store\references;%LIBPATH%
:appendlibpath
@if exist "%FrameworkDir%%Framework40Version%" set LIBPATH=%FrameworkDir%%Framework40Version%;%LIBPATH%
@if exist "%FrameworkDir%%FrameworkVersion%" set LIBPATH=%FrameworkDir%%FrameworkVersion%;%LIBPATH%
@if exist "%FrameworkDir64%%Framework40Version%" set LIBPATH=%FrameworkDir64%%Framework40Version%;%LIBPATH%
@if exist "%FrameworkDir64%%FrameworkVersion64%" set LIBPATH=%FrameworkDir64%%FrameworkVersion64%;%LIBPATH%
这样我们就不需要额外的指定了,编译器和链接器会根据这些环境变量找到需要的头文件和库函数。
一个完整的例子
有了上面的基本只是,我们就可以开始实际的开发了,不过真正的项目开发,一般不会直接通过执行cl.exe之类的来完成,而是使用Makefile文件,然后通过nmake(相当于Linux下的make)来进行编译。同样以HelloWorld.c为例,下面是对应的一个简单的Makefile文件:
all:
cl.exe HelloWorld.c
clean:
@if exist HelloWorld.obj del /f /q HelloWorld.obj
@if exist HelloWorld.exe del /f /q HelloWorld.exe
之后执行VS_COMPILE.cmd。现在文件的目录结构如下:
然后进入到HelloWorld目录下,执行nmake命令:
编译成功并顺利执行。
总结说明
以上就是一个较为完整的通过Visual Studio提供的编译工具进行代码开发的例子。
关于Makefile可以在参看【Makefile】基础和【Makefile】函数。
上面的脚本和代码可以在https://gitee.com/jiangwei0512/VisualStudioProject下载到。