预处理器如何知道将 HEADER_H 转换为 header.h?

How does the preprocessor know to translate HEADER_H to header.h?

根据 this question,您似乎可以灵活地编写它--

#ifndef _HEADER_H

或:

#ifndef __HEADER___H__

等这不是一成不变的。

但我不明白为什么我们一开始就使用下划线。为什么我不能只写:

#ifndef header.h

这有什么问题吗?为什么我们到处都加下划线并把所有东西都大写?预处理器对下划线做了什么?

因为#ifdef#ifndef后面需要一个预处理器符号,并且这些符号不能包含点。

C11 (latest draft) spec n1570 (§6.10.1) 中:

Preprocessing directives of the forms

# ifdef identifier new-line group opt

# ifndef identifier new-line group opt

check whether the identifier is or is not currently defined as a macro name.

并且标识符不能包含点 (§6.4.2.1)

顺便说一句,include guards 不需要有与文件名相关的 #ifdef 符号。如果需要,您可以使用 #ifndef JESUISCHARLIEHEBDO
#ifndef I_LOVE_PINK_ROSES_BUT_NOT_YELLOW_ONES 预处理器指令保护头文件 foo.h。但按照 human 惯例,这些名字通常是相关的。 请注意,以下划线开头的标识符是实现定义的,因此您应该避免 #ifndef _FOO_INCLUDED 而更喜欢 #ifndef FOO_INCLUDED

header.h 不是有效的标识符。宏名称中不能有句点。

也就是说,您为包含防护宏选择的名称完全是任意的。毕竟,这只是另一个变量。在文件之后命名它们纯粹是惯例(为了避免冲突也是合理的)。

我鼓励你大声说出 header 结构,看看预处理器做了什么。

#ifndef MY_HEADER_H    /* If the macro MY_HEADER_H is not defined (yet)... */
#define MY_HEADER_H    /* ... then define it now ... */

...                    /* ... and deal with all this stuff ... */

#endif                 /* ... otherwise, skip all over it and go here. */

如果将 MY_HEADER_H 替换为 I_REALLY_LIKE_BANANAS 或其他任何内容,您会发现此机制同样有效。唯一的要求是它是一个有效的宏标识符,并且不与任何其他包含保护的名称冲突。

在上面的例子中,宏被定义为空。这很好,但这不是唯一的选择。第二行同样可以读

#define MY_HEADER_H 1

然后将宏定义为 1。有些人这样做,但它并没有真正添加任何东西,而且值 1 是相当随意的。我一般不这样做。唯一的好处就是如果定义成1,除了#ifdef.

,还可以用#if

最后一点警告:以下划线开头或包含两个或多个连续下划线字符的标识符保留用于实现,不应在 user-code 中使用。因此,_MY_HEADER_H__MY_HEADER__H__都是不幸的选择。

预处理器找到正确 header 文件的逻辑

#include <myheader.h>

完全无关。这里,myheader.h 命名一个文件,预处理器将在多个目录中搜索它(通常可以通过 -I 命令行选项配置)。只有 它找到并打开文件后,它才会继续解析它,因此,它最终会找到 include guards,如果它已经解析,这将导致它基本上跳过文件它之前(并且因此已经定义了 include guard 宏,因此第一次检查的计算结果为 false)。