【问题标题】:Source code build confusion (preprocessing and linking)源代码构建混淆(预处理和链接)
【发布时间】:2017-10-21 12:12:39
【问题描述】:

为什么在预处理步骤中,主文件中的#includes 只替换为相关头文件的内容(而不是函数定义(.cpp 文件))?

我认为在此步骤中,它应该首先进入头文件并将其中的#includes 替换为其关联的 .cpp 文件的内容,然后才返回将主文件中的#includes 替换为一切,因此不需要任何链接(一个包含所有内容的巨大文件)。为什么不是这样呢?

【问题讨论】:

  • 因为你每次都会编译很多次代码。每次都会重新编译未更改的 CPP 文件。使用预编译库加快编译时间是不可能的。
  • 另外,通常的方法可以让你使用你甚至没有源代码的功能。
  • "...它们关联的 .cpp 文件"。 C++ 语言中没有这样的关联。您可以调用头文件iostream(就像 C++ 标准库所做的那样)并定义在任意数量的源文件中声明的内容,无论您喜欢什么名称。

标签: c++ build linker preprocessor


【解决方案1】:

为什么在预处理步骤中,主文件中的#includes 只替换为相关头文件的内容(而不是函数定义(.cpp 文件))?

简单地说,头文件是您告诉预处理器的唯一文件。它不能假定源文件的名称,因为任何给定的标头都可能有许多源文件。您可能会想“嘿,我为什么不直接包含源文件?”我在这里告诉你不!不好! 此外,谁说你一开始就可以访问源文件?

编译器了解和编译所有源文件的唯一方法是让编译器向编译器传递每个源文件,让它将它们编译成对象,然后将这些对象链接到一个库或可执行文件中。

【讨论】:

    【解决方案2】:

    compiling and linking有很大区别:

    预处理器、预编译时和编译时:

    预处理器检查#符号并将其替换为相关内容,例如:

    #include <iostream> // the content will be replaced here and this line will be removed
    

    所以上面会添加iostream的内容。

    eg2:

    #define PI 3.14 // wherever PI is used in your source file the macro will be expanded replacing each PI with the constant value 3.14 
    

    编译器只检查syntax errors,functionsprototypes... and doesn't care about the body of functions, resulting in an.obj 文件`。

    链接时间:

    链接器将这些obj files 与相关库链接起来,此时functions called 必须有一个定义;没有定义将导致链接时错误。

    【讨论】:

      【解决方案3】:

      为什么不这样呢?

      有经验的程序员的历史和期望,以及他们对大型程序的经验(见下面最后的陈述)


      我认为在这一步中它应该首先进入标题 文件并将其中的#includes替换为它们的内容 关联的 .cpp 文件 ...

      如果您接受使用 C++ 编码的工作,您的公司将向您提供编码标准,详细说明您想要遵循的指导方针或规则,或者在您偏离它们时为您的选择辩护。

      您现在可能需要一些时间来查看可用的编码标准。例如,尝试学习 Google C++ Style Guide(我不是特别喜欢或不喜欢这个,它很容易记住)。一个简单的谷歌搜索也可以找到几个编码标准。添加“为什么要符合编码标准?”您的搜索可能会提供一些信息。


      无需任何链接(一个包含所有内容的巨大文件)。

      注意:这种方法不能消除与编译器工具或第三方提供的库的链接。我经常使用 -lrt 和 -pthread,有时也使用 -lncurses、-lgmp、-lgmpxx 等。

      目前,作为一个实验,您可以手动实现巨型文件的方法(我经常为我的小型试验和私人工具的开发这样做)。

      考虑:

      如果 main.cc 有:

      #include "./Foo.hh" // << note .hh file
      
      int main(int argc, char* argv[])
      {
         Foo  foo(argc, argv);
      
         foo.show();   
      ...
      

      而 Foo.cc 有

      #include "./Foo.hh"  // << note .hh file
      
      // Foo implementation
      

      这是常见的模式(不,不是模式书模式),并且需要您将 Foo.o 和 main 链接在一起,这对于小型构建来说是微不足道的,但还有更多工作要做。

      “小”特性让您可以使用#include 轻松创建“一个包含所有内容的巨型文件”:

      将主要更改为

      #include "./Foo.cc"   // << note .cc also pulls in  .hh
                            // (prepare for blow back on this idea)    
      
      int main(int argc, char* argv[])
      {
         Foo  foo(argc, argv);
      
         foo.show();   
      ...
      

      编译器在一个编译单元中查看所有代码。不需要本地 .o 的链接(但仍然是库链接)。


      注意,我不建议这样做。为什么?

      可能主要原因是我的许多工具都有 100 个对象(即 100 个 .cc 文件)。那个单一的“巨型”文件可能非常巨大。

      对于大多数开发流失(即早期错误修复),只有一个或两个 .cc 文件得到更改。重新编译所有源代码可能会浪费您的时间和编译器的时间。

      替代方案是开发人员已经学到的经验:

      A) 编译数量少得多的已更改的 .cc(可能是一两个?),

      B) 然后将它们与其他 100 个未更改的 .o 链接起来会更快地构建。

      您的工作效率的一个主要关键是最大限度地减少您的编辑-编译-调试时间。 A 和 B 以及一个好的编辑器对于这个开发迭代很重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-14
        • 1970-01-01
        • 2011-08-05
        • 2013-03-24
        • 1970-01-01
        • 1970-01-01
        • 2013-01-16
        相关资源
        最近更新 更多