我想知道为什么#ifndef
指令之后的名字总是全部大写并且似乎与实际头文件的名称不匹配?这有什么规则?我一直在寻找网络,但我没有找到任何解释。如果我的头文件名为myheader.h,那么可以使用:
#ifndef MYHEADER
如果是这样,为什么?有什么规则?
答案 0 :(得分:6)
这些是预处理程序符号,没有这样的规则。 (只要它们与标题中的#defines
匹配)
但是,惯例是对预处理器符号使用全大写。
答案 1 :(得分:6)
没有“规则”,只有惯例。第一个也是最常用的约定是所有预编译宏都是大写的,所以标题保护也应该都是大写的。
至于宏名称,我使用的(以及我见过的大多数代码使用的)只是标题的名称(如上所述,转为全部大写),包括扩展名,用下划线替换点,然后是_INCLUDED
。
#ifndef MYHEADER_HPP_INCLUDED
#define MYHEADER_HPP_INCLUDED
// ...
#endif
请注意,许多前缀带有下划线或双下划线的标识符,但这不是一种好的做法,因为标准指定标识符开头(或包含)双下划线和以单下划线开头后跟大写字母的标识符保留用于所有范围内的编译器/库特定的东西(例如VC ++中的__declspec或标准头中使用的宏);以单个下划线开头的所有其他标识符都保留在全局范围内。因此,不应使用此类标识符来避免冲突。
有关此内容的更多信息here。
答案 2 :(得分:1)
不需要全部上限。这只是常见的惯例。我通常使用像#ifndef MYHEADER_H_INCLUDED
这样的东西。
答案 3 :(得分:1)
谷歌 “include guard” ,以找到实际情况。
关于全部大写:对于全大写的宏来说,这是一个 约定 。原因是宏是由预处理器处理的,这是一个神秘的文本处理工具,对C ++一无所知,最好不要使用常见的标识符,以免它遍及它们,造成一大堆混乱。
答案 4 :(得分:1)
我们的想法是确保您的头文件在构建期间只读取一次。实现这一目标的成语是结构:
#ifndef _SOME_UNIQUE_NAME
#define _SOME_UNIQUE_NAME
/* The actual header code */
#endif
这意味着您应该选择一个您非常确定将是唯一的名称,并且该名称是#ifndef的有效标识符。您还应确保标识符未在实际代码中使用或与变量或其他内容混淆。具有大写标记清楚地标记了成语。除此之外,仅仅是惯例而不是决定选择的语言。 Visual Studio的向导生成类似于标识符的GUID。 Sone编译器支持#pragma一次具有相同的效果。
答案 5 :(得分:0)
您可以使用任何您想要的名称,但是您希望使其唯一,以便不会在标题之外定义值,因此使用带有大写的标题名称只是一个很好的约定来确保。
答案 6 :(得分:0)
这是完全主观的,除了通常与用于命名预处理器宏的字符集相关的规则之外,没有强制规则。传统的宏以大写字母定义。这有助于他们在源代码中脱颖而出。我倾向于坚持的一个约定是文件名的严格大写版本,句点由下划线和前导和尾随下划线代替。因此,对于名为DataTableNameMangler.hpp
的文件,包含警卫将如下所示:
#ifndef _DATATABLENAMEMANGLER_HPP_
#define _DATATABLENAMEMANGLER_HPP_
...
#endif // _DATATABLENAMEMANGLER_HPP_
没有很好的理由,但我强烈建议一致性,名称与文件名完全匹配。我通常使用一个小类创建者脚本来生成我的初始类。以下Bash片段给出了一个想法:
#!/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 ++编译器,如果没有注释,它会抱怨它。