【问题标题】:What MSVC++ 2008 Express Editon compiler does and doesn'tMSVC 2008 Express Edition 编译器做什么和不做什么
【发布时间】:2011-06-15 13:36:34
【问题描述】:

考虑到以下示例,我一直想知道 msvc++ 2008 编译器是否会处理同一文件的多个标头包含:
ma​​in.cpp

#include "header.h"
#include "header.h"

编译器会多次包含这个文件还是只包含一次? (我知道我可以使用#ifndef“技巧”来防止这种情况发生) 另外,如果我包含包含 10 个函数的“header.h”,但我只调用或使用 2 个,它仍然会包含所有 10 个还是只包含我需要的 2 个以及他们的所有需求?

【问题讨论】:

    标签: c++ visual-studio-2008 compiler-construction compiler-optimization


    【解决方案1】:

    #include 基本上是“复制粘贴”的同义词。如果您执行相同的#includes,则该头文件的内容将被依次复制和粘贴两次。

    关于你的第二个问题,这真的没有意义。 #includes 由 preprocessor 执行,它在 compilerlinker 之前运行。预处理器不知道也不关心头文件的内容是什么,它只是将它复制粘贴进去。链接器可能能够消除不必要的功能,但这完全独立于预处理器。

    【讨论】:

    • 你怎么能确定 MSVC 不会把它当作某种预处理优化来处理? :)
    • @Armen:嗯,我不能确定!但我无法想象它会做这样的事情,因为在某些情况下你真的想#include 多次(例如 X 宏)。
    【解决方案2】:

    不,编译器(或更准确地说,预处理器)不会“自动”处理这个问题。不在 Visual C++ 2008 或任何其他版本中。而你真的不希望它这样做。

    有两种标准方法可以解决这个问题。您应该选择其中之一。

    第一个称为包含警卫。这就是您在问题中提到的“#ifndef 技巧”。但这当然不是“诡计”。这是编写 C++ 代码时处理这种情况的标准习惯用法,任何查看您的源文件的其他程序员几乎肯定期望在某处看到包含保护。

    另一个利用 VC++ 特性(也发现了它在其他几个 C++ 工具包中的方式)以更易于键入的方式完成基本相同的事情。通过在头文件顶部包含行#pragma once,您可以指示预处理器在每个翻译单元中仅包含头文件一次。这个has some other advantages 包括警卫,但它们在这里并不是特别相关。

    至于您的第二个问题,链接器 将负责“优化”您从未在代码中调用的函数。但这是编译的最后阶段,与#include无关,由预处理器处理,如上所述。

    【讨论】:

    • 好的,谢谢:)。所以只是为了把事情弄清楚:为了防止多次包含同一个头文件,我必须实现一个所谓的“包含保护”(感谢您在我的“程序员词典”中添加一个新词;))并且链接器将删除“不必要的" 最终可执行文件中的代码?
    • @Benjamin:这两件事无关。 C++ 中的编译发生在几个不同的阶段。 #include 指令在编译的 first 阶段由预处理器解析。正如 Oli 的回答所暗示的那样,它本质上是将标头的内容复制并粘贴到您的源文件中。这就是为什么您需要包含警卫(或#pragma once)来防止它多次复制和粘贴它。在编译的后期阶段,称为链接阶段,链接器将查看您调用的函数并仅包含您实际使用的函数。
    • @Benjamin:阅读this question 的答案,以更好地了解所有步骤的实际工作原理。
    【解决方案3】:

    MSVC 20xx 预处理器(不是编译器——编译器永远不会看到预处理器指令)在任何意义上都不会“处理”同一文件的多个#include。如果一个文件被#included 两次,则预处理器遵循#includes 并包含该文件两次。 (想象一下,如果预处理器甚至考虑尝试纠正源文件的“不良”#include 行为,那会是多么混乱。)

    由于预处理器在遵循您的指示时非常细致和小心,每个#included 文件都必须保护自己不被#included 两次。当我们在头文件的顶部找到这样的行时,我们会看到这种保护:

     #ifndef I_WAS_ALREADY_INCLUDED   // if not defined, continue with include
     #define I_WAS_ALREADY_INCLUDED   // but make sure I'm not included again
    
        [ header-file real contents ]
    
     #endif  // I_WAS_ALREADY_INCLUDED
    

    当你写一个头文件时,你必须始终确保以这种方式保护它。

    【讨论】:

      【解决方案4】:

      你为什么在乎?它并没有给编译器增加太多负担,因为编译器有条件地(例如使用#ifdefs)排除了不需要编译的代码。

      【讨论】:

        【解决方案5】:

        预处理器将包含 2 倍这些标头。这就是为什么需要头文件中的守卫。 据我所知,链接器在大多数情况下会删除用于减小可执行文件大小的较新代码(函数)。

        【讨论】:

          猜你喜欢
          • 2021-11-17
          • 1970-01-01
          • 2019-02-21
          • 2019-03-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-11
          • 1970-01-01
          相关资源
          最近更新 更多