【问题标题】:C++ #ifndef for include files, why is all caps used for the header file?C ++ #ifndef用于包含文件,为什么头文件全部使用大写?
【发布时间】:2010-09-26 19:39:17
【问题描述】:

我想知道为什么#ifndef 指令后面的名称总是全部大写并且似乎与实际头文件的名称不匹配?围绕这个的规则是什么?我一直在寻找网络,但我没有找到任何解释。如果我的头文件名为 myheader.h 是否可以使用:

#ifndef MYHEADER

如果是这样,为什么?规则是什么?

【问题讨论】:

    标签: c++ c-preprocessor header-files


    【解决方案1】:

    这些是预处理器符号,没有这样的规则。 (只要它们与标题中的#defines 匹配)

    但是,惯例是对预处理器符号使用全大写。

    【讨论】:

    • 谢谢!当然!现在对我来说很明显,不知道为什么以前没有打动我。它是在 ifndef 之后定义的,所以.. 哈,谢谢!
    【解决方案2】:

    没有“规则”,只有约定。第一个也是最常用的约定是所有的预编译器宏都是大写的,所以标头保护也应该都是大写的。

    至于宏名称,我使用的(以及我见过的大多数代码使用的)只是标题的名称(如前所述,转为全大写),包括扩展名,用下划线替换点,然后是_INCLUDED。

    #ifndef MYHEADER_HPP_INCLUDED
    #define MYHEADER_HPP_INCLUDED
    // ...
    #endif
    

    请注意,许多在此类标识符前加上下划线或双下划线,但这不是一个好的做法,因为标准规定标识符开头(或包含)双下划线,而那些以单个下划线后跟大写字母开头的标识符是保留的用于所有范围内的编译器/库特定的东西(例如 VC++ 中的 __declspec 或标准头文件中使用的宏);以单个下划线开头的所有其他标识符都保留在全局范围内。因此,不应使用此类标识符来避免冲突。

    更多信息here。

    【讨论】:

      【解决方案3】:

      不需要全部大写。这只是常见的约定。我通常使用#ifndef MYHEADER_H_INCLUDED之类的东西。

      【讨论】:

      • 谢谢你,终于成功了。我只是(奇怪地)没有看到定义在 ifndef 之后。我以某种方式查看了错误的地方,认为文件名将被解析为全部大写的 ifndef。
      【解决方案4】:

      谷歌 "include guard" 可以找到这件事的真正含义。

      关于全大写:宏是全大写的约定。原因是宏是由预处理器处理的,这是一种神秘的文本处理工具,对 C++ 一无所知,最好将通用标识符拒之门外,以免它践踏它们,造成大混乱。

      【讨论】:

      • 是的,我意识到常量的约定。但不知何故错过了#ifndef 之后的#define。
      【解决方案5】:

      这个想法是确保你的头文件在构建过程中只被读取一次。实现这一点的成语是结构:

         #ifndef _SOME_UNIQUE_NAME
         #define _SOME_UNIQUE_NAME
         /* The actual header code */
         #endif
      

      这意味着您应该选择一个您确信是唯一的名称并且是#ifndef 的有效标识符。您还应该确保标识符没有在实际代码中使用或与变量或其他东西混淆。有一个大写的标签清楚地标记了成语。除此之外,决定这种选择的仅仅是惯例而不是语言。 Visual Studio 的向导会生成一个类似于标识符的 GUID。 Sone 编译器支持 #pragma once 具有相同的效果。

      【讨论】:

      • 避免标题保护中的下划线(通常,对于标识符,当后跟大写字母时);详情见我的回答。
      • 更准确地说,避免在标题保护中使用前导或双下划线。
      【解决方案6】:

      这完全是主观的,除了那些通常与命名预处理器宏的字符集相关的规则外,没有任何强制规则。宏通常以大写形式定义。这往往有助于他们在源代码中脱颖而出。我倾向于坚持的一个约定是文件名的严格大写版本,句点替换为下划线以及前导和尾随下划线。因此,对于名为 DataTableNameMangler.hpp 的文件,包含保护看起来像:

      #ifndef _DATATABLENAMEMANGLER_HPP_
      #define _DATATABLENAMEMANGLER_HPP_
      
      ...
      
      #endif // _DATATABLENAMEMANGLER_HPP_
      

      虽然我强烈建议名称与文件名完全匹配,但我强烈建议这样做没有什么很好的理由。我通常使用一个小的类创建器脚本来生成我的初始类。下面的 Bash sn-p 给出了一个思路:

      #!/bin/bash
      INC_GUARD_NAME="_${1^^*}_HPP_"
      echo "#ifndef $INC_GUARD_NAME"
      echo "#ifndef $INC_GUARD_NAME"
      echo
      echo "class $1 {};"
      echo
      echo "#endif // $INC_GUARD_NAME"
      

      因此:

      $ ./makeclass.bash DataTableNameMangler
      #ifndef _DATATABLENAMEMANGLER_HPP_
      #ifndef _DATATABLENAMEMANGLER_HPP_
      
      class DataTableNameMangler {};
      
      #endif // _DATATABLENAMEMANGLER_HPP_
      

      这自然只是一个非常基本的例子。重要的是,请记住将注释放在最后一行的守卫名称之前。 #endif 不带任何参数,因此宏将被传递给 C++ 编译器,如果没有注释,它会抱怨它。

      【讨论】:

      • 避免标题保护中的下划线(通常,对于标识符,当后跟大写字母时);详情见我的回答。
      【解决方案7】:

      你可以使用任何你想要的名字,但你想让它唯一,这样值就不会被定义在你的标题之外,所以使用大写的标题名称只是一个很好的约定来确保这一点。

      【讨论】:

        猜你喜欢
        • 2010-12-11
        • 2022-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-12
        • 1970-01-01
        相关资源
        最近更新 更多