笔者在WIN10和VC6.0下学习的笔记。
在VC安装目录C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin中就是IDE编译套件。 目录文件信息如下:
cl.exe link.exe c1.dll c2.dll c1xx.dll其中cl为编译器(command line),其实现位于c1.dll c2.dll c1.dll.而link.exe 便是链接工具.
首先一个最简单的hello.cpp如下:
#include <stdio.h> void main(){ printf("bb"); printf("aa"); }编译的步骤大致分为四步骤: 1.预编译 2.编译 3.汇编 4.链接
关于这几步网上到处是轮子,详情请参阅:c编译流程
我们在vc提供的编译套件中手动实现这几步。
cl /p hello.cpp 预编译会将include展开放入生成的hello.i文件中.(关于更多编译选项 可输入 cl -help查看或c编译选项)
#line 1 "Cpp1.cpp" #line 1 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" int __cdecl _fileno(FILE *); #line 307 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" int __cdecl _flushall(void); FILE * __cdecl fopen(const char *, const char *); int __cdecl fprintf(FILE *, const char *, ...); int __cdecl fputc(int, FILE *); int __cdecl _fputchar(int); int __cdecl fputs(const char *, FILE *); size_t __cdecl fread(void *, size_t, size_t, FILE *); FILE * __cdecl freopen(const char *, const char *, FILE *); int __cdecl fscanf(FILE *, const char *, ...); int __cdecl fsetpos(FILE *, const fpos_t *); int __cdecl fseek(FILE *, long, int); long __cdecl ftell(FILE *); size_t __cdecl fwrite(const void *, size_t, size_t, FILE *); int __cdecl getc(FILE *); int __cdecl getchar(void); int __cdecl _getmaxstdio(void); char * __cdecl gets(char *); int __cdecl _getw(FILE *); void __cdecl perror(const char *); int __cdecl _pclose(FILE *); FILE * __cdecl _popen(const char *, const char *); int __cdecl printf(const char *, ...); int __cdecl putc(int, FILE *); int __cdecl putchar(int); int __cdecl puts(const char *); int __cdecl _putw(int, FILE *); int __cdecl remove(const char *); int __cdecl rename(const char *, const char *); void __cdecl rewind(FILE *); int __cdecl _rmtmp(void); int __cdecl scanf(const char *, ...); void __cdecl setbuf(FILE *, char *); int __cdecl _setmaxstdio(int); int __cdecl setvbuf(FILE *, char *, int, size_t); int __cdecl _snprintf(char *, size_t, const char *, ...); int __cdecl sprintf(char *, const char *, ...); int __cdecl sscanf(const char *, const char *, ...); char * __cdecl _tempnam(const char *, const char *); FILE * __cdecl tmpfile(void); char * __cdecl tmpnam(char *); int __cdecl ungetc(int, FILE *); int __cdecl _unlink(const char *); int __cdecl vfprintf(FILE *, const char *, va_list); int __cdecl vprintf(const char *, va_list); int __cdecl _vsnprintf(char *, size_t, const char *, va_list); int __cdecl vsprintf(char *, const char *, va_list); #line 363 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" FILE * __cdecl _wfsopen(const wchar_t *, const wchar_t *, int); #line 369 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" wint_t __cdecl fgetwc(FILE *); wint_t __cdecl _fgetwchar(void); wint_t __cdecl fputwc(wint_t, FILE *); wint_t __cdecl _fputwchar(wint_t); wint_t __cdecl getwc(FILE *); wint_t __cdecl getwchar(void); wint_t __cdecl putwc(wint_t, FILE *); wint_t __cdecl putwchar(wint_t); wint_t __cdecl ungetwc(wint_t, FILE *); wchar_t * __cdecl fgetws(wchar_t *, int, FILE *); int __cdecl fputws(const wchar_t *, FILE *); wchar_t * __cdecl _getws(wchar_t *); int __cdecl _putws(const wchar_t *); int __cdecl fwprintf(FILE *, const wchar_t *, ...); int __cdecl wprintf(const wchar_t *, ...); int __cdecl _snwprintf(wchar_t *, size_t, const wchar_t *, ...); int __cdecl swprintf(wchar_t *, const wchar_t *, ...); int __cdecl vfwprintf(FILE *, const wchar_t *, va_list); int __cdecl vwprintf(const wchar_t *, va_list); int __cdecl _vsnwprintf(wchar_t *, size_t, const wchar_t *, va_list); int __cdecl vswprintf(wchar_t *, const wchar_t *, va_list); int __cdecl fwscanf(FILE *, const wchar_t *, ...); int __cdecl swscanf(const wchar_t *, const wchar_t *, ...); int __cdecl wscanf(const wchar_t *, ...); FILE * __cdecl _wfdopen(int, const wchar_t *); FILE * __cdecl _wfopen(const wchar_t *, const wchar_t *); FILE * __cdecl _wfreopen(const wchar_t *, const wchar_t *, FILE *); void __cdecl _wperror(const wchar_t *); FILE * __cdecl _wpopen(const wchar_t *, const wchar_t *); int __cdecl _wremove(const wchar_t *); wchar_t * __cdecl _wtempnam(const wchar_t *, const wchar_t *); wchar_t * __cdecl _wtmpnam(wchar_t *); #line 414 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" #line 415 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" #line 418 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" //省略一大批 int __cdecl fcloseall(void); FILE * __cdecl fdopen(int, const char *); int __cdecl fgetchar(void); int __cdecl fileno(FILE *); int __cdecl flushall(void); int __cdecl fputchar(int); int __cdecl getw(FILE *); int __cdecl putw(int, FILE *); int __cdecl rmtmp(void); char * __cdecl tempnam(const char *, const char *); int __cdecl unlink(const char *); #line 463 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" } #line 467 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" #pragma pack(pop) #line 471 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" #line 473 "C:\\Program Files (x86)\\Microsoft Visual Studio\\VC98\\include\\stdio.h" #line 2 "hello.cpp" void main(){ printf("bb"); printf("aa"); }第一个问题,我们的include<stdio.h>会首先去环境变量的include寻找的对应的头文件,如果没有找到那么将去编译目录 也就是hello.cpp本文件所在目录寻找stdio.h。
笔者所在电脑的include环境变量:
C:\Program Files (x86)\Microsoft Visual Studio\VC98\atl\include; C:\Program Files (x86)\Microsoft Visual Studio\VC98\mfc\include; C:\Program Files (x86)\Microsoft Visual Studio\VC98\include如果开发者用双引号寻找头文件(include "")那么首先会去编译目录 helllo.cpp寻找,如果找不到才会寻找环境变量include。
另外可以在预编译的文件中可以看到导入的头文件具体位置。
笔者并没有在vc编译选项中找到,所以我就干脆把这两步骤合为一步。
cl \c hello.i 其中\c选项为编译但是不连接,hello.i 是预处理后的文件. 运行后目录将生成一个 二进制hello.obj文件,此文件可以用winhex查看。
c语言要运行必须链接实现的实现库,比如我们的include<stdio.h>的printl函数,只是在预处理的时候导入的声明然而具体的实现其没有,所以我们必须借助link.exe链接并生成可执行文件。
我们查看msdn文档的printf函数,了解到printf 实现库存在三个库中,分别为LIBC,LIBCMT,MSVCRT,分别对应单线程, 多线程,Microsoft visual C RUN TIME版本实现,我们这里只需要单线程版本。
所以 我们键入如下命令link hello.obj LIBC.LIB完成最终的程序并生成Hello.exe