【问题标题】:Understanding C++ Compilation了解 C++ 编译
【发布时间】:2011-03-17 17:13:06
【问题描述】:

我最近意识到,一般来说,我不知道 c/c++ 编译器是如何工作的。我承认这最初来自于试图理解标头守卫,但后来意识到我缺乏编译的工作原理。

以Visual C++为例;有“头文件”文件夹、“资源文件”文件夹和“源文件”文件夹。这些文件夹的分离以及您放入其中的内容有什么意义吗?对我来说,它们都是源文件。取代码sn-ps:

片段 1

//a1.h
int r=4;

//a1.cpp
int b  //<--semicolon left out on purpose

//main.cpp
#include <iostream>
#include "a1.h"
void main()
{
   cout << r;
}

编译器错误提示“a1.cpp(3) : fatal error C1004: unexpected end-of-file found” 我预计不会因为 a1.cpp 文件在 main 方法中没有#included存在于下一个代码sn-p的位置

片段 2

//a1.h
int r=4 //<--semicolon left out on purpose

//a1.cpp
int b = 4;  

//main.cpp
#include <iostream>
void main()
{
   cout << b;
}

因为“main.cpp(6) : error C2065: 'b' : undeclared identifier”而出错。如果你像这样包含 a1.cpp

片段 3

//a1.h
int r=4 //<--semicolon left out on purpose

//a1.cpp
int b = 4;  

//main.cpp
#include <iostream>
#include "a1.cpp"
void main()
{
   cout << b;
}

编译器抱怨“a1.obj : error LNK2005: "int b" (?b@@3HA) already defined in main.obj”。 sn-ps 2 和 3 都忽略了 int r = 4 没有缺少分号的事实,因为我怀疑它与它的 xxxx.h 文件有关。如果我从 sn-p 1 上的项目中删除 a1.cpp 文件,那么它编译得很好。显然,我所期望的不是我得到的。有很多关于如何在 cpp 中编码的书籍和教程,但 cpp 在编译过程中处理文件和源代码的方式并不多。这到底是怎么回事?

【问题讨论】:

  • 你需要对头文件和源文件的区别有一些非常基本的了解,加上“项目”(或Makefile)的含义。你能从读K&R的书开始吗?它不是 C++,但你的代码也不是——它是纯 C(除了
  • 我总是建议 C/C++ 新手使用命令行编译器并在进入 IDE 之前手动链接代码。了解编译/链接/执行过程在 C/C++ 中非常重要。坐下来,放松一下,GCC 一会儿。
  • @Brandon 你的意思是在 linux/unix 环境中吗?或者会有windows端口吗?另外,我想看什么样的文学作品?
  • 您可以使用 MinGW 或 Cygwin,但 Visual Studio 构建在 MS 的编译器之上。 C++ 版本称为 cl.exe。 msdn.microsoft.com/en-us/library/f35ctcxw(v=vs.71).aspx至于文学(stackoverflow.com/questions/388242/…)

标签: c++ ide header compiler-errors build-system


【解决方案1】:

您的问题并不是关于编译器,而是关于您的 IDE 如何处理整个构建系统。大多数 C/C++ 项目的构建系统分别编译每个 .c.cpp 文件,然后将生成的目标文件链接在一起成为最终的可执行文件。在您的情况下,您的 IDE 正在编译您在项目中具有的文件扩展名为 .cpp 的任何文件,然后链接生成的对象。您看到的行为可以解释如下:

  1. a1.cpp 缺少 ;,因此当 IDE 尝试编译该文件时,您会收到有关“文件意外结束”的错误。

  2. b 未在 main.cpp 编译单元中的任何位置声明,因此您会收到有关未定义标识符的错误。

  3. b 存在于main.cppa1.cpp 编译单元中(显然在a1.cpp 中,并且通过您的#include 用于main.cpp)。您的 IDE 编译这两个文件 - 现在 a1.omain.o 每个都包含一个名为 b 的对象。链接时出现重复符号错误。

这里要带走的重要一点,它解释了您看到的所有行为,是您的 IDE 编译 每个 .cpp 文件 - 而不仅仅是 main.cpp 和它包含的文件 - 和然后链接生成的对象。

我建议使用您自己创建的 makefile 设置一个命令行测试项目 - 它将向您介绍构建系统的内部工作原理,然后您可以将这些知识应用到 IDE 的内部工作原理中。

【讨论】:

    【解决方案2】:
    1. 头文件未编译
    2. #include 指令从字面上粘贴可包含文件的内容,而不是 #include 行
    3. 所有源文件(与 main 无关)都编译为 .o 或 .obj 文件。
    4. 所有 obj 文件与外部 .lib 文件(如果有)链接在一起
    5. 您将获得一个可执行文件。

    关于第2点:

    example
    //a.h
    int
    
    //b.h
    x = 
    
    //c.h
    5
    
    //main.cpp
    #include <iostream>
    int main()
    {
    #include "a.h"
    #include "b.h"
    #include "c.h"
    ;
    
    std::cout << x << std::endl; //prints 5 :) 
    }
    

    这不是一个完整的答案,而是 hth、my2c 等 :)

    【讨论】:

      【解决方案3】:

      既然好像有两种理解你的问题的方法,我就回答理解C++编译部分。

      我建议您首先阅读 Wikipedia 中的“编译器”定义。之后,尝试谷歌搜索编译器教程来建立你对编译器的理解。更具体到 C++,您可以阅读 #include 和预处理器指令(尝试 Google 搜索这些术语。)

      如果您还想进一步了解编译器,我建议您阅读编译器书籍。您会在 StackOverflow 上找到一份不错的书籍清单。

      【讨论】:

      • 居然订了另一个贴子引用的“龙书”,就等邮件中了:)
      • @hydroparadise:不错的选择。它很密集,但解释得很好。
      【解决方案4】:

      #include 语句将该文件插入到生成#include 的文件中。因此,您的 sn-p 3 main.cpp 在编译之前变为以下内容。

          // main.cpp    
          // All sorts of stuff from iostream
          //a1.cpp
          int b = 4;
          void main()
          {
              cout << b;
          }
      

      您收到链接器错误的原因是您定义了 b 两次。它在 a.cpp 和 main.cpp 中定义。

      您可能希望阅读有关声明和定义的内容。

      【讨论】:

        【解决方案5】:

        您告诉构建系统要编译哪些文件。对于 Visual C++,它将自动编译您添加到项目中的任何名为“*.cpp”的文件。尽管您可以进入项目设置并告诉它不要这样做。

        不会编译名为 *.h 的文件(尽管如果你明确告诉它它可以。

        #include 指令是编译器在进行任何编译之前处理的东西(它被称为预处理器)。它基本上采用它指向的文件并将其粘贴到正在编译的源文件中,此时#include 指令出现在文件中。然后编译器将整个东西编译为一个完整的单元。

        所以,在你的例子中:

        片段 1

        Bote a1.cpp 和 main.cpp 由构建系统单独编译。所以,当它遇到错误om a1.cpp时,它会报告它。

        片段 2

        注意它是单独编译这些文件的,彼此不知道,所以当你在main.cpp中引用b时,它并不知道b是在a1.cpp中定义的。

        片段 3

        现在您已将 a1.cpp 包含在 main.cpp 中,因此它编译 main.cpp,看到 b 的定义并说,好的,我在全局范围内有一个 b。然后它编译 a1.cpp,然后说 OK,I have a b at global scope。

        现在链接器介入并尝试将 a1 和 main 放在一起,它现在告诉你,嘿,我有 2 个 b 吃全局范围。不好。

        【讨论】:

          【解决方案6】:

          编译器从你告诉它的地方获取源文件。在 Visual C++ 的情况下,有一个 IDE 告诉编译器要做什么,并且存在不同的文件夹,因为这就是 IDE 组织文件的方式。

          另外,sn-p 2 中的错误来自链接器,而不是编译器。 编译器已将 main.cpp 和 a1.cpp 编译到目标文件 main.obj 和 a1.obj 中,然后链接器试图生成一个结合这些目标文件的可执行文件,但变量 b 在 a1.obj (直接)和main.obj (通过 a1.cpp 的包含),所以你会得到“已经定义”的错误。

          【讨论】:

            【解决方案7】:

            您在案例 1 和 3 中看到的问题是 VS 特定的。 VS 显然试图编译 main.cpp 和 a1.cpp。

            案例1:当VS尝试编译a1.cpp时,出现语法错误(缺少分号),编译失败。

            案例 2:您没有在 main.cpp 或任何包含的文件中声明变量 b。因此编译失败。

            情况 3:这是一个链接器错误。由于包含,int b 已在 main.cpp 和 a1.cpp 中声明。由于它们都不是静态的或外部的,因此两个具有相同标识符的全局变量已在同一范围内声明。这是不允许的。

            【讨论】:

              猜你喜欢
              • 2014-08-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多