【问题标题】:Preprocessor directive #ifndef for C/C++ code用于 C/C++ 代码的预处理器指令 #ifndef
【发布时间】:2010-05-11 15:56:50
【问题描述】:

在 Eclipse 中,每当我创建一个新的 C++ 类或 C 头文件时,我都会得到以下类型的结构。假设我创建了头文件 example.h,我得到了这个:

/*Comments*/
#ifndef EXAMPLE_H_
#define EXAMPLE_H_
/* Place to put all of my definitions etc. */
#endif

我认为 ifndef 是说如果未定义EXAMPLE_H_,请定义它,这可能有用,具体取决于您用于编译和链接项目的工具。但是,我有两个问题:

  1. 这相当普遍吗?我不经常看到它。使用该标准是个好主意吗,还是应该直接开始定义代码。

  2. EXAMPLE_H_ 到底是什么?为什么不 example.h,或者只是示例?有什么特别之处吗,或者可能只是 eclipse 更喜欢自动构建项目的产物?

【问题讨论】:

  • 请注意,一切都在 ifndef 和 endif 之间,在任何情况下,您作为应用程序程序员都可能会遇到。因此,不仅仅是 #define 被定义和包含一次,而是所有内容都只被定义和包含一次。
  • 好点,抱歉没有说得更清楚,谢谢。

标签: c++ c eclipse c-preprocessor


【解决方案1】:

这是一个常见的结构。目的是在翻译单元中只包含一次头文件的内容,即使物理头文件被包含多次。例如,如果您将标头直接包含在源文件中,并且它也通过另一个标头间接包含,则可能会发生这种情况。

将#ifndef 包装在内容周围意味着编译器只解析头的内容一次,并避免重新定义错误。

一些编译器允许“#pragma once”做同样的事情,但 #ifndef 构造在任何地方都有效。

【讨论】:

  • 啊,好吧,所以你的#define 语句只是用来定义一些东西,我可以在这两种情况下将EXAMPLE_H_ 替换为FOO_BAR_,它仍然可以工作,最好还是按原样使用EXAMPLE_H_与命名空间冲突相比,命名空间冲突的可能性要小得多,是吗?
  • 对,通常人们使用带有下划线的标题名称来表示标识符中无效的字符。使用可能会发生冲突的通用名称会破坏目的:)
  • @Leif Andersen:像 example 这样的常见名称可能会发生冲突。就我个人而言,我用完整的命名空间作为文件名的前缀,因为这不太可能发生冲突。我看到其他人生成完整的 GUID 以避免发生冲突的可能性。
【解决方案2】:

这只是保护您的包含的常用方法 - 这样可以防止代码被包含两次。使用的标识符可以是任何东西,只是按照描述的方式进行操作。

【讨论】:

    【解决方案3】:

    这很常见吗?是的 - all C 和 C++ 头文件应该是这样的结构。示例_H 是一个标头保护,它可以防止标头中的代码在同一个翻译单元中多次包含,这会导致多个定义错误。选择名称 EXAPMLE_H 以匹配它所保护的头文件的名称 - 它需要在您的项目中是唯一的,并且可能也是全局的。为了确保这一点,通常在项目名称前加上前缀或后缀:

    #define MYPROJ_EXAMPLE_H
    

    例如,如果您的项目名为“myproj”。顺便说一句,不要想着用下划线作为前缀会神奇地使它独一无二 - 像 _EXAMPLE_H___EXAMPLE_H__ 这样的名称是非法的,因为它们是为语言实现保留的。

    【讨论】:

      【解决方案4】:

      始终在头文件的顶部执行此操作。它通常被称为标头保护或包含保护。

      它的作用是使如果一个头文件被包含多次,它只会被包含一次。如果你不这样做,那么你最终会遇到关于事物被多次定义的错误以及类似的事情。

      确切的定义并不重要,尽管通常它是文件名的一些变体。基本上,您正在检查是否已定义给定的宏。如果没有,则定义它,并继续包含该文件。如果有,那么您之前一定已经包含了该文件,并且该文件的其余部分将被忽略。

      【讨论】:

        【解决方案5】:

        这是一个包含守卫。它保证包含的标头不超过一次。

        例如,如果您要:

        #include "example.h"
        #include "example.h"
        

        第一次包含标头时,EXAMPLE_H_ 不会被定义,而是会进入 if 块。然后EXAMPLE_H_#define 指令定义,并对标头的内容进行评估。

        第二次包含标头时,EXAMPLE_H_ 已定义,因此不会重新进入 if 块。

        这对于确保您不违反单一定义规则至关重要。如果您在没有包含保护的标头中定义了一个类并且包含该标头两次,则由于违反了一个定义规则(该类将被定义两次)而导致编译错误。

        虽然上面的例子很简单,你可以很容易地看到你包含了example.h 两次,但标题经常包含其他标题,这并不那么明显。

        【讨论】:

        • 不,它没有!它保证它不超过一次:-)
        【解决方案6】:

        考虑一下

        文件 foo.c:

        #include foo.h
        #include bar.h
        

        文件栏.h

        #include <iostream>
        #include foo.h
        

        现在,当我们编译 foo.c 时,我们有两次 foo.h!我们绝对不希望这样,因为所有函数都会在第二次抛出编译错误。

        为了防止这种情况,我们将INCLUDE GUARD 放在顶部。这样,如果它已经被包含,我们定义一个预处理器变量来告诉我们不要再次包含它。

        这很常见(通常是强制性的),如果有人不把它放在那里,会非常令人沮丧。 should 可以简单地期望每个 .h 文件在包含时都有一个标头保护。当然,当你假设事情时,你知道他们会说什么(“让你和我成为混蛋”),但这应该是你期望看到的。

        【讨论】:

          【解决方案7】:

          这称为include guard,确实是 C/C++ 头文件的常用习惯用法。这允许头文件被包含多次,而无需多次包含其内容。

          名称EXAMPLE_H_ 是一个任意约定,但必须遵守 C 预处理器宏的命名规则,其中不包括像 example.h 这样的名称。由于 C 宏都定义在单个全局命名空间中,因此重要的是不要有不同的头文件使用相同的名称作为它们的包含保护。因此,在包含保护名称中包含项目或库的名称通常是个好主意:

          #ifndef __MYPROJECT_EXAMPLE_H__
          ...
          

          【讨论】:

          • 除非以下划线开头的名称后跟大写字母或另一个下划线保留给实现(即,不要以下划线开头包含保护名称)。
          • @James 以及任何连续包含两个下划线的标识符(即不仅仅是在开头)。也就是说,这个答案充满了违规。 ;)
          猜你喜欢
          • 1970-01-01
          • 2019-01-20
          • 2021-11-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-26
          相关资源
          最近更新 更多