代码之家  ›  专栏  ›  技术社区  ›  John Rudy

#在实践中不定义?

  •  14
  • John Rudy  · 技术社区  · 16 年前

    名称可能未定义为 #undef , 通常是为了确保一个例行程序 实际上是一个函数,而不是宏:

    #undef getchar

    int getchar(void) { ... }

    #define -是否使用与函数同名的宏?或者这真的是一个现实中不会出现的样本?(例如,无论是对的、错的还是精神错乱的人都不应该重写 getchar() ,所以它不应该出现。)对于您自己的函数名,您觉得有必要这样做吗?如果您正在开发一个供其他人使用的库,这种情况会改变吗?

    8 回复  |  直到 10 年前
        1
  •  16
  •   Jonathan Leffler    7 年前

    它做什么

    如果你读了普劳格的 The Standard C Library (1992),你会看到 <stdio.h> 允许标头提供 getchar() getc() 作为类似宏的函数(具有 getc() 多次计算其文件指针参数!)。但是,即使它提供了宏,实现也必须提供执行相同任务的实际函数,主要是为了您可以访问名为 getchar() 并将其传递给其他函数。

    也就是说,通过这样做:

    #include <stdio.h>
    #undef getchar
    
    extern int some_function(int (*)(void));
    
    int core_function(void)
    {
       int c = some_function(getchar);
       return(c);
    }
    

    如前所述 core_function() 这是毫无意义的,但它说明了这一点。你也可以用这个做同样的事情 isxxxx() <ctype.h> 例如,我也是。

    通常,您不想这样做-您通常不想删除宏定义。但是,当你需要真正的函数时,你可以得到它。提供库的人可以很好地模拟标准C库的功能。

    很少需要

    还请注意,您很少需要使用显式 #undef 是因为您可以通过以下方式调用函数而不是宏:

    int c = (getchar)();
    

    因为代币 getchar 这不是一个 ( .

    如果您使用宏重写实现自己的函数,则可以使用它来获得良好的效果,尽管除非解释,否则可能会有点混淆。

    /* function.h */
    …
    extern int function(int c);
    extern int other_function(int c, FILE *fp);
    #define function(c) other_function(c, stdout);
    …
    
    /* function.c */
    
    …
    
    /* Provide function despite macro override */
    int (function)(int c)
    {
        return function(c, stdout);
    }
    

    function 不是 . 这个 return 行调用宏。

        2
  •  14
  •   Konrad Rudolph    13 年前

    宏通常用于生成大量代码。它通常是一种非常本地化的用法,而且可以安全地使用 #undef

    /编辑:作为一个例子,我用它来为我生成结构。以下是一个实际项目的摘录:

    #define MYLIB_MAKE_PC_PROVIDER(name) \
        struct PcApi##name { \
            many members …
        };
    
    MYLIB_MAKE_PC_PROVIDER(SA)
    MYLIB_MAKE_PC_PROVIDER(SSA)
    MYLIB_MAKE_PC_PROVIDER(AF)
    
    #undef MYLIB_MAKE_PC_PROVIDER
    
        3
  •  6
  •   Adam Rosenfield    16 年前

    因为预处理器 #define 都在一个全局名称空间中,很容易产生名称空间冲突,特别是在使用第三方库时。例如,如果要创建一个名为 OpenFile <windows.h> 定义令牌 打开文件 OpenFileA OpenFileW (视乎情况而定) UNICODE 定义与否)。正确的解决办法是 #undef 在定义函数之前。

        4
  •  2
  •   mipadi    16 年前

    我只在 #included 文件正在干扰我的一个函数(例如,它具有相同的名称)。然后我 #undef 宏,以便我可以使用自己的函数。

        5
  •  2
  •   Jonathan Leffler    16 年前

    虽然我认为乔纳森·莱弗勒给了你正确的答案。这里有一个非常罕见的例子,我使用了一个#unde。通常,宏应该在许多函数中可重用;这就是为什么要在文件顶部或头文件中定义它。但有时函数中有一些重复的代码,可以用宏来缩短。

    
    int foo(int x, int y)
    {
    #define OUT_OF_RANGE(v, vlower, vupper) \
        if (v < vlower) {v = vlower; goto EXIT;} \
        else if (v > vupper) {v = vupper; goto EXIT;}
    
        /* do some calcs */
        x += (x + y)/2;
        OUT_OF_RANGE(x, 0, 100);
        y += (x - y)/2;
        OUT_OF_RANGE(y, -10, 50);
    
        /* do some more calcs and range checks*/
        ...
    
    EXIT:
        /* undefine OUT_OF_RANGE, because we don't need it anymore */
    #undef OUT_OF_RANGE
        ...
        return x;
    }
    

        6
  •  1
  •   John Millikin    16 年前

    这是一种常见的做法来防止有人定义与函数同名的宏吗?或者这真的是一个现实中不会出现的样本?(例如,无论是对的、错的还是疯狂的,都不应该重写getchar(),所以它不应该出现。)

    两者都有一点。好的代码不需要使用 #undef ,但是有很多糟糕的代码需要处理。 当有人耍这样的把戏时会证明是无价之宝 #define bool int .

        7
  •  1
  •   Michael Burr    16 年前

    除了修复宏污染全局名称空间的问题外,还有 #undef 宏可能需要在不同的位置具有不同的行为。这不是一个真正常见的场景,但我想到的是:

    • 这个 assert 明确肯定 自身需要 #未定义 NDEBUG 明确肯定

    • 我见过一种技术,通过使用宏将变量声明为 extern ,但对于使用头/声明定义变量的单一情况,宏将被重新定义为nothing。

    /* globals.h */
    /* ------------------------------------------------------ */
    #undef GLOBAL
    #ifdef DEFINE_GLOBALS
    #define GLOBAL
    #else
    #define GLOBAL extern
    #endif
    
    GLOBAL int g_x;
    GLOBAL char* g_name;
    /* ------------------------------------------------------ */
    
    
    
    /* globals.c */
    /* ------------------------------------------------------ */
    #include "some_master_header_that_happens_to_include_globals.h"
    
    /* define the globals here (and only here) using globals.h */
    #define DEFINE_GLOBALS
    #include "globals.h"
    
    /* ------------------------------------------------------ */
    
        8
  •  0
  •   Community CDub    5 年前

    如果可以定义宏,则必须有一个用于取消定义的工具。

    我使用的内存跟踪器定义了自己的new/delete宏来跟踪文件/行信息。此宏将中断SC++L。

    #pragma push_macro( "new" )
    #undef new
    #include <vector>
    #pragma pop_macro( "new" )
    

    盲目地取消定义宏将增加混乱,降低可维护性,并可能破坏依赖于原始行为的东西。如果您是被迫的,至少使用push/pop在其他地方保留原始行为。