• 【LookHere!】这个编译错误是什么意思咩qaq


    最近一直有同学问为何会出现这种编译错误,既然需求如此之大,我就写篇文章解释一下吧。

    先解释一下,代码在编译的时候,你的电脑中发生了一件什么事吧。

    编译指的就是把人类可以理解的文本程序“翻译”成电脑可以识别执行的指令清单,充当翻译官的角色的就是编译器。

    一般来说,童鞋使用的devc++内部包含了g++/gcc/c++等等,这些都是编译器,可以在devc++的安装目录里找到,如图

    其实编译器就是一个可以运行的程序,它接受你的源代码作为输入,然后输出的就是它的翻译结果啦~~

    但是呢,编译器小姐姐是傲娇属性爆棚的那种人~一旦你的源代码中有语法错误,她就不会给你翻译(编译),但是小姐姐人辣么好,她会把你的错误之处向你指出。

    所以学习看编译信息就是和小姐姐交朋友的第一步啦~如果你哪个编译信息看不懂,可以把它丢到搜素引擎里去搜搜,一般来说看懂了就能知道哪儿出错了。

    而今天的主题是,这种编译错误是什么意思,然后要如何解决这个问题。

    泥萌回忆一下,在学着把代码拆分到不同文件里然后用include合并到一起之前是不是从来没见过这种错误呢?

    include的作用其实非常简单,就是把你include的文件内容替换你的include那句语句。这也就解释了为何不能A include B然后B include A,想啊,这样替换下去不就无限循环了嘛;以及为什么不能多次include同一个文件,想啊,这不就相当于你的程序里可能会出现两个一毛一样的函数、变量嘛。

    这一替换是在编译之前发生的,所以小姐姐在编译时就可以找到你定义在其他文件(file2.cpp)里的函数,从而允许你在当前文件里使用(file1.cpp)。

    这本来是极其愉快而简单的一件事,可是泥萌的助教小姐姐不让泥萌include cpp文件

    因为啊,通常来时被incude的叫“头文件”,而头文件一般包含的是类的定义、extern变量声明和函数的声明等等声明性、定义性的东西,而不是具体实现,这样会带来两个显著的好处:

    • 保证所有文件使用的是同一个声明,这样修改起来很方便;只需要修改一处,而不用因为工程需求变动(以后泥萌会想吃掉一个叫PM的人的)而去修改分散在每个文件中的定义。是不是时曾相识?对的,这个理念在泥萌学用define来对常量进行宏定义就出现过了。
    • 方便支持预编译头文件,这个的意思是,当泥萌的工程成长到一个极其大的规模时,编译时间会变得非常感人,因为小姐姐的翻译速度也是有限的啊。所以为了减少小姐姐的工作量,我们可以把代码拆开,然后就可以分开编译,最后把它们并到一起,一旦有细微的改动,只需要修改需要修改的函数实现,然后重新编译改动过的文件,再把它们合并到一起。

    请泥萌仔细地看一下第二点,然后我来解释一下为什么头文件不应该包含具体实现

    一般具体实现放在cpp文件里,这也是为何我们称后缀为.h的文件为头文件而不叫.cpp文件头文件。

    假设你修改了某个函数的具体实现,那么显然,包含了这个修改的文件必须重新编译对吧,因为你会include cpp文件,那么include了这个cpp文件的文件也得重新编译,假设还有其他文件include你include这个cpp文件的cpp文件,这个文件也得重新编译,再假设...

    这样的话第二点的存在意义在哪??

    解决方案就存在于被include的头文件只应该包含声明和定义。

    但是如果只包含只有声明文件的头文件,会发生什么呢?

    会发生——小姐姐虽然找得到函数的声明,但是她找不到函数的实现!这就是造成文前那个问题的元凶!

    let's take a closer look on what 小姐姐 has said.

    undefined reference to 'YourFunction'

    请大家体会一下undeclared(声明)和undefined(定义)的区别!

    让我来画个图:

    (箭头的意思代表include)

    小姐姐看到你让她帮你编译一下main.cpp,于是她在编译开始前把declare.h放到main.cpp里。

    一切看起来都非常和谐,因为一切在main.cpp中调用的函数、使用的自定义变量类型的声明都可以找到~

    小姐姐极其开心呐~

    但是当小姐姐开始编译时,听听她怎么说:“伦家当场就懵逼了好嘛qaq”

    因为她找不到你函数的实现呐,你函数的实现在define.cpp里,这件事情她怎么可能会知道呢?

    除非你告诉她。

    所以解决方案其实非常简单,修改你的编译命令,把define.cpp的所在告诉泥萌可怜的小姐姐

    好的,我忘记先向泥萌解释编译命令是啥了。

    编译命令就是你告诉小姐姐应该按照什么样的顺序、什么样的方式、什么样的程度来进行编译,要知道其实虽然一直把编译比作翻译,其实编译过程有很多个部分,请参考reference中的Linux GCC常用命令

    具体要如何修改呢?如果你是面向命令行编程(虽然我觉得。。泥萌目前大概是不会面向命令行编程的),只需要修改一下编译命令即可:g++ main.cpp define.cpp -o main.exe

    这个命令(原来是g++ main.cpp -o main.exe,注意区别)就告诉编译器小姐姐,我还有一个define.cpp文件,如果你需要找某些函数的实现,你可以考虑一下看看define.cpp里面有没有对应的实现~

    但是要如何才能直接把你的编译命令告诉编译器小姐姐呢?

    follow the instructions,老司机要发车啦~

    1. 首先,同时按住win+r,输入cmd,然后回车

    2. 对着黑乎乎的窗口输入g++,去唤醒小姐姐

      如果你看到类似的输出,说明小姐姐被唤醒哒,跳到第4步,否则继续
    3. 如果提示的是command not found,说明小姐姐还处在devcpp的魔爪下,让我来指导你来拯救她吧!
      找到你的devcpp的安装目录,这个具体位置因人而异,一般在C:Program Files (x86)下,如果你找不到,请百度一下devcpp的安装目录。。这个我难以帮忙。。
      在安装目录下有一个MinGW(64)文件夹,点击进入,里面有一个bin文件夹,我的小姐姐就安静地躺在里面呐~明眼的你能看到她吗?
      总之,找到这个路径

      ctrl+c复制一下。
      把这个路径添加到系统变量path里去,具体看http://jingyan.baidu.com/article/8ebacdf02d3c2949f65cd5d0.html
      添加之后,再从第一步开始,你就会发现小姐姐对你的召唤有反应了!!

    4. 找到你的源代码文件所在的文件夹,在空白的地方先按住shift,再单击鼠标右键,弹出的窗口中有
      在此处打开命令端口,点击,然后又会出现黑乎乎的窗口(写到这才意识到,你们应该认识这个窗口的。。)
    5. 输入g++ main.cpp define.cpp -o main.exe回车
      这就是在直接调用编译器来编译程序,得到main.exe的结果就是可执行文件,可以双击运行。
      但是你可以在命令行里继续输入main.exe回车,就可以直接执行main.exe了!
      nice and quick!
    6. 问题就解决了!我保证你不会再看到文前的那种错误了,如果有其他错误,那就和这个问题无关了。

    写到这里,我提供了一个解决方案,就是手动写编译命令,告诉小姐姐编译时去考虑define.cpp,在具体情况下,比如泥萌的作业,你要把median.cpp student_info.cpp fails_list.cpp等等放到编译命令里去再编译。

    写这篇文章的意义就达成了。

    但我还是想多说一些导向性的东西,比起这些东西,技术细节其实并不重要。

    1. 编译过程(预处理、编译、链接)是个很值得花时间理解的东西,请自行找些资料读读,以后课程会有《编译原理》,教你如何自己写一个编译器(不过我旦此课水平令人忧心忡忡)
    2. 以后随着你需要考虑的文件越来越多,每次编译写编译命令就是个令人生厌的过程了,你会经历:手写.bat批处理文件、使用Makefile、回到ide的怀抱(强烈推荐VS2017)
    3. 请大家遇到问题之后,先上网搜搜,相信我,大多数遇到的问题前人都遇到过,比如这次的问题,搜undefined reference就能搜到解释的;之所以提出这一点不是说不欢迎大家来问问题,但是这样做显然会节约两者的时间对吧~嘻嘻,表扬

    大概写了三个小时。。第一次写这种东西。。希望对大家有点帮助吧,写模电作业去qaq。。。我的周末。。真是充实呐苦笑。。

    reference(further reading materials!):

    g++ 编译命令的简单解释,请读一读 https://courses.cs.washington.edu/courses/cse373/99au/unix/g++.html

    C++文件包含处理#include http://c.biancheng.net/cpp/biancheng/view/148.html

    C++中头文件(.h)和源文件(.cpp)都应该写些什么 http://blog.csdn.net/lyanliu/article/details/2195632

    C++编译链接过程 http://blog.csdn.net/edisonlg/article/details/7081357

    Linux GCC常用命令 http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2287738.html

    http://man.linuxde.net/gcc

  • 相关阅读:
    AdminLTE 3.0发布了
    .NET Core ORM 类库Petapoco中对分页Page添加Order By对查询的影响
    【译】ASP.NET Core在 .NET Core 3.1 Preview 1中的更新
    物品编码中心所说的包装指示符
    Entity Framework Core生成的存储过程在MySQL中需要进行处理及PMC中的常用命令
    Asp.Net Core Mvc Razor之RazorPage
    基于Asp.Net Core MVC和AdminLTE的响应式管理后台之侧边栏处理
    Qt的下载地址
    由于MicrosoftVisualStudio14.0DesignerShadowCache导致的一个异常问题
    Spring系列.事务管理原理简析
  • 原文地址:https://www.cnblogs.com/ichn/p/6684819.html
Copyright © 2020-2023  润新知