【问题标题】:C++ header guard syntaxC++ 标头保护语法
【发布时间】:2016-11-10 23:24:34
【问题描述】:

对于文件名啊.h...我一直用

#ifndef A_H
#define A_H
#endif

但现在我在教程中看到了

#ifndef __A_H_INCLUDED__ 
#define __A_H_INCLUDED__ 
#endif

还有

#ifndef _A_H_INCLUDED 
#define _A_H_INCLUDED
#endif

所以我想知道发生了什么事...这在 C++ 从未有过的版本中发生了变化,还是这个编译器依赖于这个编译器?

我使用 MinGw (gnu gcc g++) 和 msvc...

编辑:

如果对为什么要问这个问题感到困惑,我相信这对新手来说是相当困惑的。例如,对于名为 a.h 的文件,我们不会说

#ifndef a.h
#define a.h

甚至

#ifndef A_H
#define A_H a.h

这对我来说很有意义。

相反,我们说 A_H... 或上述变体之一。因此,我认为不仅仅是任何语法都可以。我一直认为它必须是大写的文件名,并且必须用_替换句点。这就是我多年来看到它被教导的方式。不,我看到像 _INCLUDED 之类的词......这显然不是 a.h 文件名的一部分......这令人困惑......编译器如何协调 A_H_INCLUDED 指的是 a.h?我不明白为什么这被否决了,我认为这很令人困惑。

另外,有人说其中一些变体不符合标准,因此我认为这是一个有效的问题,不应该被否决。

还是#define A_H 将所有代码从头文件转储到A_H 宏中?但我认为定义以换行符结束......在这种情况下,我们定义 A_H 没有值。在这种情况下, A_H 只是一个没有值的名称,问题仍然是当您使用像 A_H_DINGDONG 这样的时髦空 marco 变量时,编译器如何知道您正在谈论文件 a.h ?

【问题讨论】:

  • 你在说什么?宏名称一直是完全任意的。选择你喜欢的任何东西。除了名称之外,我认为这三个变体之间没有区别。但是,我建议不要使用以__ 或_+大写字母开头的名称 - 这些都是保留的。
  • 避免以下划线开头可能会干扰系统库
  • 你可以使用任何你喜欢的marco定义。还有#pragma once,它不是标准的,但几乎所有 C/C++ 编译器都支持。
  • 例如,如果您的宏:__WIN32 那么您将面临冲突

标签: c++ c++11 c-preprocessor c++14


【解决方案1】:

语言没有相关变化。包含保护中使用的标识符只是一个标识符。实际上整个包含保护机制并不是由语言定义的;这只是一个约定。

问题在于,以两个下划线或一个下划线和一个大写字母开头的标识符被保留给实现。这意味着,如果您在代码中使用名称 __FOO_H__,至少有可能编译器已经为自己的内部目的预定义了它,并且您的代码可能会以某种不确定的方式中断。

教你写作的教程:

#ifndef __A_H_INCLUDED__ 
#define __A_H_INCLUDED__ 
#endif

给你的建议很糟糕。

还有一个微妙之处:以E 和一个数字或E 和一个大写字母开头的宏名称保留用于<errno.h> / <cerror> 标头。因此,如果您的头文件名为 earth.h 并且您编写:

#ifndef EARTH_H
#define EARTH_H
#endif

那么您可以原则上与EARTH_H 错误号发生冲突。

这不太可能发生,_H 后缀几乎可以肯定足以阻止它,但我用来完全避免它的约定是:

#ifndef H_EARTH
#define H_EARTH
#endif

你建议:

#define a.h

宏名称必须是单个标识符。这实际上定义了带有值.h 的宏a。

#define A_H a.h

由于宏仅使用#ifndef 进行测试,因此它具有什么值并不重要,只关心它是否已定义。

【讨论】:

    【解决方案2】:

    #ifndef、#define 和 #endif 是重要的语法部分。您使用什么命名约定取决于您。

    【讨论】:

      【解决方案3】:

      这些只是变量名之类的名称。

      双下划线名称是保留的,所以严格来说#define __A_H_INCLUDED__是未定义的行为和下划线+大写字母shouldn't be used either。

      您只是想避免与您的其他代码和标准的命名冲突,并且使用文件名通常可以确保这一点。

      【讨论】:

        【解决方案4】:
        #define A_H
        

        与文件a.h没有任何关联。

        在dir/filename.h内,你可以:

        #ifndef WHATEVER_NOT_DEFINED_HENCE_HAVE_DIR_FILENAME_H_IS_GOOD_
        #define WHATEVER_NOT_DEFINED_HENCE_HAVE_DIR_FILENAME_H_IS_GOOD_
        #endif
        

        但是你不能用这个

        #define WHATEVER_HAS_DOUBLE_UNDERSCORE_LIKE_THIS_IS_BAD__
        #define __OR_LIKE_THIS_IS_BAD
        #define OR_LIKE__THIS_IS_BAD
        

        你不能有一个标识符以下划线开头,然后是这样的大写字符:

        #define _LIKE_THIS_IS_BAD
        

        【讨论】:

          【解决方案5】:

          这归结为个人/组织偏好,尽管对此有一些约定。一般来说,放在这里的东西并不重要,只要它没有在其他任何地方使用。

          一些例子:

          • A_H:看起来类似于您将在代码中使用的常量。
          • A_H_:会更好,因为您不太可能看到末尾带有下划线的常量。
          • _A_H_INCLUDED:乍一看,这个看起来更好,因为它在外观上类似于私有文件范围变量。 但是这是个问题,因为下划线+大写字母是保留的,所以这不是一个合适的宏。
          • A_H_INCLUDED:没有前导下划线,目的明确,不太可能与程序中的其他任何内容发生冲突。
          • __A_H_INCLUDED__:双下划线保留;不要使用。

          此外,它不是标准的一部分,但#PRAGMA ONCE 得到了广泛的支持,并且完成了同样的事情,根本不需要担心命名约定。

          【讨论】:

          猜你喜欢
          • 2020-04-30
          • 2013-11-17
          • 2011-06-13
          • 2013-11-13
          • 2015-10-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-03-15
          相关资源
          最近更新 更多