从零开始,学习windows编程 - hello.c的疑惑!

我们现在有的,是一个hello.c文件,通过cl命令就可以生成目标hello.exe文件。

那我们就可以研究一下cl这个东西。

CL解释

CL.EXE(或cl.exe,似乎windows的文件系统对于大小写是不敏感的),是何许人也呢?

因为VC6的版本说明没有找到,这里的说明文字是Visual studio 2013的CL.EXE说明,不过都是一样的。

CL是用来控制微软C和C++编译器(compiler)和链接器(linker),一共做了两件事情。编译器会生成COFF文件格式的目标文件(.obj);链接器会生成可执行文件(.exe)或者动态链接库文件(DLLs)。

对比我们之前的做法,的确是执行cl命令之后,生成了一个hello.obj文件和一个hello.exe文件。

选项的使用

数了一下,有99个选项之多。每个选项都有自己的作用,就需要使用的时候去熟悉了,不过我们现在不需要了解这么多,我们就来看下/c选项好了。

This option prevents the automatic call to LINK. Compiling with /c creates .obj files only. You must call LINK explicitly with the proper files and options to perform the linking phase of the build.

Any internal project created in the development environment uses the /c option by default.

To set this compiler option in the Visual Studio development environment

This option is not available from within the development environment.

To set this compiler option programmatically

This compiler option cannot be changed programmatically.

要彻底理解它,就必须知道C语言到目标代码的过程。

▓▓首先,C语言中有两种文件,一个.c文件(我们一般称之为源文件),一个.h文件(我们一般称之为头文件)。头文件一般会被源文件包含,而源文件会被编译成一个二进制中间文件,之后所有的二进制中间文件会通过链接器进行链接,形成一个可以在某个特定系统上执行的二进制可执行文件。

于是,C语言-->可执行代码可以明显的分为编译过程和链接过程,而由于C语言中一种特殊语法--“宏”的存在,所以还有一个预编译过程,在预编译过程中,C语言中的宏会被展开;同时,包含的头文件中的代码,会根据条件编译等,囊括到源文件中,这些工作做好后,给编译器的就是一份新的源码文件,供编译阶段使用。

另外,相信大家都知道,C语言是从汇编语言发展过来的,汇编是从机器码发展过来的,以前的编译器都是先将C语言编译成汇编语言,再将其解释成机器码就可以了。现代编译器除了做这个,还可以对代码进行优化。不过基本思想是一直没有变的。

而我们做C开发的时候,有时稍微看一下汇编之后的代码,对于计算机是如何执行程序的过程,会有更清晰的认识。

那么,用cl可以生成汇编代码吗?答案当然是肯定的。

在命令行输入cl /FA hello.c,看看生成了哪些东西?当然,如果你不要生成.exe文件,可以加上/c选项。

可以看到生成了.obj文件和.asm文件。

.asm的汇编代码,仅仅用cl再进行编译,我还没有找到方法,所以这里就要引入一个新工具--ml,ml是MASM中编译汇编代码使用的编译器,同样,它也会调用到link,并且让它不调用link,使用的编译选项也是/c。

从cl和hello.c,我们引出了hello.obj,hello.asm,以及ml.exe,包括C语言到可执行文件的过程。下面先跳过ml,来看看链接过程。

 

链接过程

链接过程也就是将.obj变成.exe的过程,使用的是link.exe。

直接使用cl /c hello.c产生的hello.obj文件,输入命令link hello.obj,直接就生成了.exe文件。

真简单~不过,且慢,我们有main函数,但是至少printf函数这个东西,是在哪里呢?

秘密就藏在生成的那个.obj文件中啦!如果用另外一个.obj文件(稍候我们会用另外一个hello.obj),那就会得到错误哦。

那我们打开obj文件来看看吧。怎么?还要看二进制文件?不用担心,看的还是英文字符,多说无用,还是看下就知道了。

这里我使用的是winhex,其实使用其他任何一个文本编辑器都是可以的,不过看二进制文件我还是习惯使用专门的二进制编辑器。截图用的picpick。顺便说明下。

左边是文件内容,主要看右边,红线划过的地方。看不清的同学可以看这里:

-defaultlib:LIBC –defaultlib:OLDNAMES

其实,链接过程会将所有的静态库和目标文件进行链接,而在这份hello.obj文件中,指定了两个default lib,一个为libc.lib,一个为oldnames.lib。而这两个lib文件又是何方神圣呢?

libc.lib为单线程静态C标准库(在cl中可使用/ML选项定义对其的链接),而oldnames.lib则是为了兼容微软以前的C/C++开发系统,基本不使用了,至少在我们这个hello.c编译链接成exe的过程中,可以忽略掉它。

其实,我们用一个很简单的方法可以知道我们的程序链接了哪些库。

 d:\test>link /verbose:lib hello.obj

 Microsoft (R) Incremental Linker Version 6.00.8168

 Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

 Searching Libraries

 Searching D:\Program Files\Microsoft Visual Studio\VC98\lib\LIBC.lib:

 Searching D:\Program Files\Microsoft Visual Studio\VC98\lib\OLDNAMES.lib:

 Searching D:\Program Files\Microsoft Platform SDK\Lib\.\kernel32.lib:

 Done Searching Libraries

这里还可以使用一个小工具,叫做dumpbin的,可以利用它的选项来分析hello.obj文件,看到defaultlib,而不用打开文件自己查看。

——————————————

 d:\test>dumpbin /DIRECTIVES hello.obj

 Microsoft (R) COFF Binary File Dumper Version 6.00.81683 Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

 Dump of file hello.obj

 File Type: COFF OBJECT

 Linker Directives

 -----------------

 -defaultlib:LIBC

 -defaultlib:OLDNAMES

 Summary

 A .data

26 .drectve

14 .text

——————————

oldnames.lib我们可以略过,但是libc.lib我们还是需要了解的,今后要做VC相关项目,使用到第三方库的会经常用到。为了解释libc.lib,我们需要引入一个新的概念--“C语言运行库”,这里我们只讨论windows平台,VC中使用的被称为MSVC CRT。

另外还要牵涉到的一个概念就是线程。

 

C语言运行时库(C Run-time Library)---CRT

要了解这个东西,就需要对C语言的发展史有一定的了解。C语言是在上世纪70年代在B语言的基础上被发明出来的,之后Dennis Ritchie 和 Brian Kernighan 就用C本身重写了90%以上的 UNIX 系统函数。并且把其中最常用的部分独立出来,形成头文件和对应的 LIBRARY,C run-time library 就是这样形成的。在类UNIX系统中,C运行库被认为是系统的一部分。

而随着C语言的流行,出现了很多C编译器,也就有很多C运行时库的实现。主要有下面的几种

BSD libc, implementations distributed under BSD operating systems.

◑GNU C Library, used in GNU/Linux and GNU/HURD.

◑Dinkum C99 Library from Dinkumware, most common commercially licensed one

◑Microsoft C Run-time Library, part of Microsoft Visual C++

◑dietlibc, an alternative small implementation of the C standard library (MMU-less)

◑uClibc, a C standard library for embedded Linux systems (MMU-less)

◑Newlib, a C standard library for embedded systems (MMU-less)

◑klibc, primarily for booting Linux systems.

◑EGLIBC, variant of glibc for embedded systems.

◑musl, another lightweight C standard library implementation for Linux systems

可以看到,我们现在讨论的Microsoft C Run-time Library也是其中的一种。在那个年代,线程还没有被应用到操作系统上,应用程序都是单线程的。所以最初的C Run-time Library都是单线程的。另外动态库的概念也是后期出现的,所以一开始C Run-time Library也只是静态链接。

随着时间的推移,计算机技术也在不断进步。C语言运行时库也根据单线程、多线程、静态链接、动态链接,是否需要debug信息的不同,分为不同的版本。在微软是MSVC CRT实现中,具体是按照表格中的实现来分类的。其中,静态链接有单线程和多线程版本,动态链接仅有多线程版本。

 

使用link链接CRT

前面我们的obj文件中就包含有default lib,那如果我们的obj文件中不包含该信息呢?

首先,我们需要生成一个不包含default lib信息的obj文件。

这里有两种方法,一种是生成ASM文件,然后利用ml来生成一个obj文件,其中不包含该信息(当然ml也会主动调用link,这里也需要使用选项来控制,具体有兴趣的童鞋可以自行研究,因为这里主要还是C/C++编程为主,汇编会涉及,但是不会深入);还有一种方法是利用cl的/Zl选项,这里要注意的是l是小写的L而不是I,我一开始就是看错了,结果编译出来还是带有default lib信息的。

cl /c /Zl hello.c

之后利用dumpbin来查看一下是否生成正确。然后再调用link看看。

结果如下:

现在就找不到_printf和_mainCRTStartup这两个符号了。

之后使用link hello.obj libc.lib看看。

成功生成hello.exe!

 

总结

使用到的工具

★CL.EXE

★LINK.EXE

★ML.EXE

★DUMPBIN.EXE

★WINHEX

整个C程序编译过程, MSVC和GCC对比

 

VC文件后缀名

我是一名从事了10年开发在退休边缘垂死挣扎的高龄程序员,最近我花了一些时间整理了一个完整的学习C语言、C++的路线,项目源码和工具。对于想学习C/C++的小伙伴而言,学习的氛围和志同道合的伙伴很重要,笔者推荐一个C语言/C++编程爱好者的聚集地☞ !

欢迎初学和进阶中的小伙伴。希望你也能凭自己的努力,成为下一个优秀的程序员。工作需要、感兴趣、为了入行、转行需要学习C/C++的伙伴可以一起学习!”

关注我,带你遨游代码世界!

下面这些是C/C++能做的 :

服务器开发工程师、人工智能、云计算工程师、信息安全(白客)、大数据 、数据平台、嵌入式工程师、流媒体服务器、数据控解、图像处理、音频视频开发工程师、游戏服务器、分布式系统、游戏辅助等!

最后分享一张C/C++编程学习路线图给爱学习的小伙伴们

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值