4

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

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

片段 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 文件不是 #included 哪里存在 main 方法下一个代码片段

片段 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”。int r = 4片段 2 和 3 都忽略了没有缺少分号的事实,因为我怀疑它与其 xxxx.h 文件有关。如果我从片段 1 的项目中删除 a1.cpp 文件,那么它编译得很好。显然,我所期望的不是我得到的。有很多关于如何在 cpp 中编码的书籍和教程,但 cpp 在编译过程中处理文件和源代码的方式并不多。这到底是怎么回事?

4

7 回答 7

6

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

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

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

  3. b存在于 themain.cppa1.cpp编译单元中(显然在a1.cpp和通过你的#includefor main.cpp)。您的 IDE 编译这两个文件 - 现在a1.o每个文件main.o都包含一个名为b. 链接时,您会收到重复符号错误。

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

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

于 2011-03-17T17:20:44.220 回答
3
  1. 头文件未编译
  2. #include 指令从字面上粘贴可包含文件的内容,而不是 #include 行
  3. 所有的源文件(redargless 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 等 :)

于 2011-03-17T17:18:06.190 回答
2

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

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

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

于 2011-03-17T18:18:23.187 回答
1

#include 语句将该文件插入到生成#include 的文件中。因此,您的代码片段 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 中定义。

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

于 2011-03-17T17:25:47.737 回答
1

您告诉构建系统要编译哪些文件。对于 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 的定义并说,好的,我在全局范围内有 ab。然后它编译 a1.cpp,并说 OK,我在全局范围内有 ab。

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

于 2011-03-17T17:31:06.587 回答
0

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

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

于 2011-03-17T17:22:48.167 回答
0

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

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

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

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

于 2011-03-17T17:27:20.597 回答