美文网首页C语言技术干货程序员技术栈
C++ 写个游戏引擎—(基础篇) F

C++ 写个游戏引擎—(基础篇) F

作者: zidea | 来源:发表于2018-12-18 06:31 被阅读133次

    今天将介绍 c++ 的链接过程,那么在开始新的内容前我和大家先总结一下之前编译的相关内容。

    名词解释

    编译:把源文件中的源代码翻译成机器语言,保存到目标文件中。如果编译通过,就会把CPP转换成OBJ文件。

    编译单元:每个cpp就是一个编译单元,每个编译单元相互之间是独立且相互不知的。

    目标文件:编译后生成的文件,以机器码的形式包含了编译单元里所有的函数和数据、导出符号表、未解决符号表、地址重定向表等

    编译过程

    编译分为两个过程:

    1. 预处理阶段(宏、条件编译指令、头文件包含、特殊符号)

        2. 编译、优化阶段(针对代码优化,不依赖具体计算机、针对计算机优化)

        3  汇编

    现在给大家介绍链接

    链接:将单个编译后的文件链接成一个可执行的文件,

    编译后的 obj 编译单元彼此之间相对独立没有联系。只有link 可以相互调用。

    先准备一个项目,项目非常简单,在源文件下只有一个 main.cpp 文件,在 main.cpp 中定义两个方法 Log 和 Multiply,如下图。 

    先用 ctrl + f7 来编译 Main.cpp 文件,项目编译没有问题。然后当我们尝试"build"(生成)程序,发现报错。

    提示我们“必须定义入口文件”这是因为我们项目类型为应用(exe) 所以必须指定入口 main 函数,不然不知道应该先运行那个函数。可以在项目属性中查看到项目的类型,如下图

    我们应该注意到在(输出)提示错误的为 LNK1561 表示是链接错误而非编译错误。通过这个标识我们可以很清晰分辨是编译错误还是链接错误,这个也很重要。

    现在我们通过在 Main.cpp 中添加 main 函数来修改这个错误,

    当然项目入口函数可以叫其他名称,可在项目属性中指定一下入口函数名称即可,如下图。

    调整一下代码,运行程序,build 成功

    我们将 Log 函数提取分离到新建的 Log.cpp 文件中来整理代码。

    在此 build 项目,发生链接错误,提示找不到标识符,这个工作就是链接阶段主要做的工作。

    我们在 Main.cpp 添加声明 Log 函数,让链接可以找到对应 Log 函数。

    函数的声明一定要和函数定义保持一致(函数名称 参数类型以及数量和返回值等),如果我们将 Log 函数名修改为 Logr 然后再次 build 项目

    在 build 就会报错误

    我们尝试修改返回值,由于函数定义和声明的返回值类型不同也会报错。

    当将调用 Log 函数代码注释掉,在此 build 这时就不会报错,原因顾名思义了。 

    现在我们再想一想,如果注释掉调用 Multiply 函数,这时在build 时会不会报错呢?如下图

    build 结果是报错了,原因是,即使在这个cpp文件中(编译单元)中没有调用Multiply 这个函数,但不能排除他在其他 cpp 文件(编译单元)中被调用的可能。所以链接认为这是错误,那么如果我们能告诉链接这个 Multiply 函数只在 main.cpp 被调用。这个问题就解决了。

    解决方法就是在 Multiply 前添加 static 修饰符,表示这个函数只属于这个 cpp 编译单元。

    如果将 Log 函数复制出一份,放置在 Log 函数下面,这样就有了两个同样的 Log 函数。

    在此运行 build 时,也会报错。由于认为 Log 主体(这里主体就是函数体吧)已经存在了。

    现在将 Log 函数提取分离到一个头文件 Log.h 中去。然后修改一下 Log.cpp 。添加一个调用 Log 函数的 InitLog 函数,并且需要引用一下 Log.h ,如下图

    在 Main.cpp 中也引用一下 Log.h 文件

    运行 build 报链接错误

    为什么会报错,我们还记得include 预处理的作用吗?就是将头文件内容替换到#include该头文件的位置这样就在 Log.cpp 和 Main.cpp 两个文件(编译单元中都存在 Log 函数)。所以报了“error LNK2005: "void __cdecl Log(char const *)" (?Log@@YAXPEBD@Z) 已经在 Log.obj 中定义”我们要解决,需要在 Log.h 中 Log 函数前加上 static 修饰符。这表示 Log 函数仅属于引用文件(单元)的内部,就不会报错了。

    有几种方法可以解决上面的错误,

    1. 为 Log.h 中的 Log 函数添加 static 修饰符,表示引用此函数时,此函数仅属于该编译单元(文件)内部。

    2. 函数添加 inline 修饰符,即可以把函数指定为内联函数,函数的代码被放入符号表中,在使用时直接进行替换,(像宏一样展开)。

    3. 将函数体从 Log.h 移入到 Log.cpp 中,Log.h 仅保留 Log 函数声明。

    相关文章

      网友评论

        本文标题:C++ 写个游戏引擎—(基础篇) F

        本文链接:https://www.haomeiwen.com/subject/sichkqtx.html