【问题标题】:Different versions to prevent double include of header file, which is correct?防止头文件双重包含的不同版本,哪个是正确的?
【发布时间】:2020-07-24 08:59:28
【问题描述】:

如果我有一个名为 test.h 的文件并且想要防止双重包含它(以防止链接器错误),我被告知要输入:

#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED

但我已经看到了多个版本,例如一个告诉我写:

#ifndef TEST_H
#define TEST_H

有什么区别,为什么我不能像这样简单地使用我的文件的原始名称:

#ifndef test.h
#define test.h

编辑:如果#ifndef 之后的名称不重要并且可以是类似的名称,预处理器如何知道我正在尝试防止双重包含名为test.h 的头文件:@ 987654332@,如果我不打算使用它,那么使用名称还有什么意义呢?他们应该让它更容易,让我们只输入:

#ifndef
#define

【问题讨论】:

标签: c macros include c-preprocessor c99


【解决方案1】:

前两个选项在语义上是等价的,哪个“更好”是主观的(前者更清晰,但涉及更多输入)。

第三个选项是不可能的,因为C语言中的宏只能包含字母、数字(但不能作为第一个字符)和下划线。规则与任何其他类型的标识符(例如,变量和函数名称)相同;有关详细信息,请参阅此 cppreference 页面。

【讨论】:

    【解决方案2】:

    有什么区别[..]

    没有区别。宏名称可以是任何名称,约定用于以下划线分隔的标头名称 (TEST_H)。

    为什么我不能像这样简单地使用我的文件的原始名称

    宏名称中不能包含.。所以是不允许的。标识符的相同规则也适用于宏名称。见6.4.2 Identifiers

    您可以改为:

    #ifndef test_h
    #define test_h
    #endif
    

    或:

    #ifndef test_header
    #define test_header
    #endif
    

    但同样,惯例是使用所有大写字母,将点替换为下划线,并可能使用前导/尾随下划线。


    以这个代码示例为例(不等同于宏 - 仅用于演示目的):

    int flag = 0; 
    if (flag == 0) {
        /* include this content */;
        flag = 1;
    }
    else {
        /* not included */
    }
    

    这里flag 相当于宏名。这就是为什么这个名字本身并不重要。重要的是flag 是否已设置。

    他们是否应该让它变得更容易,让我们只输入:
    #ifndef
    #定义

    很遗憾,这不是有效的语法。

    我理解一般问题并同意这应该更好/更容易。但这是历史性的。预处理器是一个古老的东西(至少从 70 年代初开始),现在不会改变 :)

    【讨论】:

      【解决方案3】:

      问题的要点似乎在编辑的摘要中得到了很好的总结:

      编辑:预处理器如何知道我试图防止双重包含名为 test.h 的头文件,如果名称之后 #ifndef 不重要,可以是:#ifndef Dummy

      预处理器知道您正在特别尝试防止多重包含,也不知道您要保护的头文件名称的任何可操作性。这是预处理器的通用条件编译机制在适应(实际上不是阻止)多重包含的特定问题上的应用。

      如果我不打算使用一个名字,那还有什么意义呢? [...]

      但你确实使用它。在#ifndef 指令中。此处引用的宏名称与 #defined 相同,这对于该机制正常工作至关重要。

      该机制利用预处理器可以测试是否定义了特定的宏名称,并将结果用作源文件的关联部分是否被编译的条件。这就是 #ifndef ... #endif 的意义所在。 #define 确保编译之间的部分会导致定义指定的预处理器宏,以便#ifndef 条件在后续包含相同标头时评估为假。因此,宏名称的具体选择并不重要,但它与其他头文件使用的所有宏名称不同(或者如果有意这样做,则不是)这一事实是重要的 .

      他们是否应该让它变得更容易,让我们只输入:

      #ifndef
      #define
      

      同样,这是通用机制的特定应用。通用机制用于定义预处理器宏并测试它们是否被定义,这是更大的预处理器宏工具的一部分。该工具根据特定的宏名称工作。此外,即使它是明智的,一个空的宏名称也不能用于此目的,因为(如果普遍使用)它不会满足使用不同宏名称的每个标题的必要标准。

      但是,一些 C 实现同意您的观点,即标头保护可能更容易。 作为一个常见(但不是通用!)扩展,一些实现承认

      #pragma once
      

      出现在标头的开头,以替代基于条件编译的包含保护。

      【讨论】:

        【解决方案4】:

        前两个定义选项没有什么不同。不接受最后一个选项,因为预处理器不接受 (.) 作为标识符的一部分。

        有时需要从几个不同的头文件中选择一个包含到您的程序中:

        #if SYSTEM_1
           # include "test1.h"
        #elif SYSTEM_2
           # include "test2.h"
        #endif
        

        您只需输入一个宏名称:

        #define SYSTEM_1 "test1.h"
        ...
        #include SYSTEM_1
        

        更新:

        你不能使用:

        #ifndef
        #define
        

        预处理器需要一个宏名称。它就像if ... else 声明。你不能if () {} else {}

        你可以定义任何你不希望事件使用的东西。

        #ifndef NOTUSE
        #define NOTUSE
        

        例如,当你要调试时,你定义DEBUG,你可以在需要的时候使用它。

        #ifdef DEBUG
        printf(" debug here\n");
        #endif /* DEBUG */
        

        如果您不定义DEBUGprintf 函数将不会运行。

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-04
        • 1970-01-01
        • 2019-06-28
        • 2016-08-05
        • 2016-02-24
        • 1970-01-01
        • 2011-03-27
        • 1970-01-01
        相关资源
        最近更新 更多