简单的HelloWorld程序其实并不简单
"#include"处为预处理语句,在#号后面的都是预处理语句,在编译之前会把里面文件所包含的内容copy到此文件中。include所包含的文件通常被称为“头文件 (header file) ”。
例如:
#include <iostream>
这句的意思就是把iostream文件里的内容拷贝到当前文件中。
每一个C++程序都有一个main函数。main函数是程序的入口。
c++程序是逐行执行
返回类型是int,但main函数比较特殊,当不需要返回值时,其默认会返回0。注意:仅对main函数起作用,此为一个特殊情况。
<< 符号叫做重载运算符,实际上是一个函数,相当于print() ,所作的操作就是将“hello world” 字符串传入cout,而cout就是将其打印在控制台里。endl就是前进到另一行。
.get() 含义是等待输入。
当预处理结束后,编译器就会将我们的代码转换为机器码。
VS中的 solution configuration (解决方案配置) 用于定义如何为这个平台编译的一系列规则; solution platfotm(解决方案平台)则是我们当前编译的目标平台,例如我们选择x86,就是生成 windows 32位系统下的可以运行的文件(x86和win32是一回事)。
所有的 .cpp 文件都会被编译,而头文件则不会,头文件只有在编译的时候其内容会被放入 .cpp 文件中。所以我们会有一堆 .cpp 文件被编译,而且他们是被一个个单独编译的。每个 cpp 文件会被编译成一个 object 文件,在Windows下的拓展名为 .obj 。这些 obj 会由链接器 (linker) 链接成一个整体,也就是最后的 exe 文件。linker会将所有的obj拿来,并将它们联系起来组成一个 exe 文件。在vs中,仅编译 cpp 文件的快捷键为 ctrl+F7 。
P.S. 不要依赖错误列表 (error list),使用输出窗口查看错误!
如果我们不想在main文件中显示太多代码,则可以将代码写在多个文件中,但是需要在使用时进行声明。
declaration(声明)
definition(定义)
linker 会将它们相互关联。在生成的时候,linker 会去找 log 的定义,然后跟 main 里调用的联系起来。如果找不到就会产生一个 linking error 的错误。
linker的工作就是 resolve symbols,联通各个函数。
本例中,linker会把log的定义拿到一个公共的binary里,也就是 hello world.exe
编译器(compiler)的工作原理
c++ 编译器唯一的功能就是要把代码文本变为从代码到程序这一过程中的中继格式——obj。之后 obj 们会被传入 linker,linker 会将其链接起来,从而生成一份可执行文件,也就是我们最后得到的程序。关于 linker 的部分之后再谈,这部分先讨论编译器的工作。
事实上,compiler 在产出这些 obj 时,做了好几件事:
首先它需要预处理(pre-process)我们的代码,也就是所有的预处理语句会在那时被评估;预处理后,我们会进入标记解释(tokenizing)和解析(parsing)阶段,这个阶段用通俗的语言来讲就是把我们所写的c++代码处理成编译器能懂和处理的语言,这个阶段的结果就是创建某种叫做抽象语法树(abstract syntax tree),也就是将我们的代码以抽象语法树的形式表达出来。说到底,编译器的工作就是把代码转化成常数资料(constant data)和指令(instruction),当编译器创建了这颗抽象语法树后,就可以产生代码了,这个代码是真正的 CPU 会执行的代码。
项目里的每个 cpp 文件都会被编译器编译成一个 obj,而这些 cpp 文件也被称为编译单元(translation unit)。
我们要有一种意识,那就是 c++ 中没有“文件”这一说法,文件只是一种用来给编译器提供源码的方法。我们需要告诉编译器文件类型和编译器该如何处理它,当我们创建一个.cpp 文件时,编译器就知道它是 c++ 的代码而不是头文件代码;同样的,当我们创建一个 .h 文件,则编译器会将它识别为头文件代码而不是 c++ 代码。这是一种默认的方式,我们完全可以自行定义(可能要对编译器进行设置或者做一些改动),但如果我们不主动告诉编译器怎么处理,编译器就会按照自己的默认规则进行处理。我们完全可以定义一个后缀名为 .chi 的文件,然后告诉编译器这是 c++ 代码,请按照 c++ 代码进行处理。所以,文件不代表任何东西,只是一种用来给编译器提供源码的方法(再次强调)。
当我们告诉编译器这个文件是c++代码时,编译器就会将其当作一个 translation unit,然后 translation unit 会得到一个 obj。在 cpp 文件 include 其它 cpp 其实时很常见的做法。实际上就是一个大的 cpp 文件里面有很多小的 cpp 文件。如果我们只编译那一个cpp文件,也就是只有一个 translation util,也就只有一个 obj。这就是为什么我们这里要用两个术语(translation unit 和 cpp文件),如果我们所编译的 cpp 文件相互没有 include ,则会生成多份 obj,然而多份 cpp 文件如果相互 include,那么我们生成的 obj 文件可能会少于 cpp 文件数。但是通常我们的解决方案(这里以vs为例)是 Debug 模式,所以每一份 cpp 文件无论是否包含,还是会生成一份 obj 文件。
有了上述的 obj 文件后,我们就可以使用链接器生成我们的程序了。