【问题标题】:What does #define do if you only have an identifer如果您只有一个标识符,#define 会做什么
【发布时间】:2016-02-09 20:08:24
【问题描述】:

通常#define 将用于定义常量或宏。但是,以下列方式使用#define 是有效的代码。

#define MAX // does this do anything?
#define MAX 10 // I know how to treat this.

所以,如果我#define MAX 10,我知道我的预处理器将 MAX 的所有实例替换为 10。如果有人单独使用 #define MAX 但没有以下替换值,则它是有效的。这真的有什么作用吗?

我问的原因是我正在用 c++ 为 c 编写一个编译器,并且需要处理预处理器指令,但我无法确定当这种情况发生时是否需要任何功能,或者我只是一旦我的预处理完成,请忽略它。

我的第一反应是,这将在我的符号表中创建一个没有名为 MAX 的值的符号,但同样可能它什么也不做。

作为补充问题,我知道这是一种不好的形式,但我真的很好奇。实际代码中是否存在会使用此类内容的情况?

谢谢, 宾克斯

【问题讨论】:

  • 澄清一下,我想知道#define SOMETHING 是如何完全独立工作的,在我的示例代码中,我直接给了它一个值,所以我知道#define MAX 在那个案子。
  • 如果你定义了什么,它就会被定义。所以如果你稍后检查它是否定义,你可以找到它是否定义。这就是重点
  • @lol4t0 我看不出这样做的目的,是不是和标识符一样对待?
  • 不。定义的名称是预处理器名称。预处理器有自己的范围,只知道/可以检查该范围内的名称
  • 你疯了:“我问的原因是我正在用 c++ 为 c 编写编译器并处理预处理器指令”。您需要其他资源,stackoverflow 对此没有好处(除非您找到另一个疯子)。

标签: c++ compiler-construction c-preprocessor


【解决方案1】:

一个典型的例子是标题保护:

#ifndef MYHEADER
#define MYHEADER
...
#endif

您可以测试是否使用#ifdef / ifndef 定义了某些内容。

【讨论】:

  • 谢谢你,是的,我不知道为什么我没有想到这一点,因为我确实使用了头卫,哈哈。
【解决方案2】:

它创建一个带有空白定义的符号,以后可以在其他预处理器操作中使用它。它可以用于以下几方面:

1) 分支。

考虑以下几点:

#define ARBITRARY_SYMBOL

// ...

#ifdef ARBITRARY_SYMBOL
    someCode();
#else   /* ARBITRARY_SYMBOL */
    someOtherCode();
#endif  /* ARBITRARY_SYMBOL */

符号的存在可用于分支,有选择地选择适合情况的代码。一个很好的用途是处理特定于平台的等效代码:

#if defined(_WIN32) || defined(_WIN64)
    windowsCode();
#elif defined(__unix__)
    unixCode();
#endif /* platform branching */

这也可以根据情况用于虚拟代码。例如,如果你想要一个只在调试时存在的函数,你可能会有这样的东西:

#ifdef DEBUG
    return_type function(parameter_list) {
        function_body;
    }
#endif  /* DEBUG */

1A) 标头守卫。

在上述基础上,如果整个标头已经包含在跨多个源文件的项目中,标头保护是一种虚拟化整个标头的方法。

#ifndef HEADER_GUARD
#define HEADER_GUARD

// Header...

#endif  /* HEADER_GUARD */

2) 模拟出一个符号。

当与分支结合使用时,您还可以使用带有空白定义的定义来虚设符号。考虑以下几点:

#ifdef _WIN32
    #define STDCALL __stdcall
    #define CDECL __cdecl
    // etc.
#elif defined(__unix__)
    #define STDCALL
    #define CDECL
#endif  /* platform-specific */

// ...

void CDECL cdeclFunc(int, int, char, const std::string&, bool);
// Compiles as void __cdecl cdeclFunc(/* args */) on Windows.
// Compiles as void cdeclFunc(/* args */) on *nix.

这样做可以让您编写独立于平台的代码,但能够指定 Windows 平台上的调用约定。 [请注意,标题 windef.h 执行此操作,将 CDECLPASCALWINAPI 定义为不支持它们的平台上的空白符号。] 这也可以用于其他情况,只要您需要预处理器符号仅在某些条件下扩展为其他内容。

3) 文档。

空白宏也可以用于记录代码,因为预处理器可以将它们去掉。微软很喜欢这种方法,在 windef.h 中使用它来表示 Windows 函数原型中常见的 INOUT 符号。

可能还有其他用途,但这些是我能想到的唯一用途。

【讨论】:

  • 您在解释这一点方面做得比我做得好得多。赞一个!
  • 谢谢。你的回答也很好,它着眼于预处理器是如何在幕后工作的。
【解决方案3】:

它不会“做”任何事情,因为它不会向代码行添加任何内容

#define MAX

int x = 1 + 2; MAX // here MAX does nothing

但空定义的作用是允许您有条件地做某些事情,例如

#ifdef DEBUG
  // do thing
#endif

类似地header guards 使用宏的存在来指示文件是否已包含在翻译单元中。

【讨论】:

    【解决方案4】:

    #define 进行逐个字符的替换。如果您没有给出任何值,那么标识符将被替换为...nothing。现在这可能看起来很奇怪。我们经常使用它来创建一个标识符,它的存在可以用#ifdef#ifndef 来检查。最常见的用途是所谓的“包含保护”。

    在您自己的预处理器实现中,我认为没有理由将其视为特殊情况。该行为与任何其他 #define 语句相同:

    1. 将符号/值对添加到符号表中。

    2. 只要出现符号,就用它的值替换它。

    最有可能的是,对于没有价值的符号,第 2 步永远不会发生。但是,如果是这样,则该符号将被简单地删除,因为它的值为空。

    【讨论】:

    • 我想你的意思是#define 不是#include
    • @callyalater 是的,感谢您的建议。现在改了。
    • 从技术上讲,预处理器会进行令牌替换。 (某些版本的 Visual Studio 除外,它们经常产生不正确的扩展。)
    • @rici 在 OP 的表面上,我想我不想深入那些需要对编译器理论有初步了解的细节。
    • @Code-Apprentice:OP 声称正在编写编译器/预处理器,因此他们更好地理解令牌是什么。
    【解决方案5】:

    C 预处理器 (CPP) 为使用 #define 宏定义的所有变量创建定义表。当 CPP 通过代码时,它至少对这些信息做两件事。

    首先,它对定义的宏进行令牌替换。

    #define MAX(a,b)  (a > b) ? (a) : (b)
    MAX(1,2); // becomes (1 > 2) ? (1) : (2);
    

    其次,它允许使用其他预处理器宏(例如 #ifdef#ifndef#undef 或 CPP 扩展(例如 #if defined(MACRO_NAME))来搜索这些定义。

    这允许在值不重要但定义标记这一事实很重要的情况下灵活地使用宏定义。

    这允许如下代码:

    // DEBUG is never defined, so this code would
    // get excluded when it reaches the compiler.
    
    #ifdef DEBUG
    // ... debug printing statements
    #endif
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-20
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 2014-02-22
      相关资源
      最近更新 更多