我想知道 #undef 在 C 中的实际用途。我正在通过 K&R 进行工作,并由预处理器负责。其中大部分内容是我(或多或少)理解的,但第 90 页(第二版)上的一些内容让我印象深刻:

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

#undef getchar

int getchar(void) { ... }

这是防御某人的常见做法吗 #define- 一个与你的函数同名的宏?或者这实际上更像是现实中不会发生的样本?(EG,任何人在他正确、错误或疯狂的头脑中都不应该重写 getchar(), ,所以它不应该出现。)使用您自己的函数名称,您觉得有必要这样做吗?如果您正在开发供其他人使用的库,这种情况会改变吗?

有帮助吗?

解决方案

它做什么

如果您阅读Plauger的标准C库(1992),您会看到允许<stdio.h>标题提供getchar()getc()作为类函数的宏(具有core_function()特殊权限,以便多次评估其文件指针参数!)。但是,即使它提供了宏,实现也必须提供执行相同工作的实际函数,主要是为了使您可以访问名为isxxxx()<ctype.h>的函数指针并将其传递给其他函数。

即通过:

#include <stdio.h>
#undef getchar

extern int some_function(int (*)(void));

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}

正如所写,#undef毫无意义,但它说明了这一点。例如,您也可以使用getchar中的(宏执行相同的操作。

通常,您不希望这样做 - 您通常不想删除宏定义。但是,当你需要真正的功能时,你可以掌握它。提供库的人可以模拟标准C库的功能,以达到良好的效果。

很少需要

另请注意,您很少需要使用显式function的原因之一是因为您可以通过编写来调用函数而不是宏:

int c = (getchar)();

因为return之后的标记不是<=>,它不是类函数宏的调用,所以它必须是对函数的引用。同样,即使没有<=>,上面的第一个例子也会正确编译和运行。

如果使用宏覆盖实现自己的功能,可以使用此功能,但除非另有说明,否则可能会有些混乱。

/* 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);
}

函数定义行不会调用宏,因为<=>之后的标记不是<=>。 <=>行确实调用了宏。

其他提示

宏通常用于生成大量代码。它通常是一个非常本地化的用法,并且在特定标题的末尾#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

由于预处理器#define s都在一个全局命名空间中,因此很容易产生命名空间冲突,尤其是在使用第三方库时。例如,如果要创建名为OpenFile的函数,则可能无法正确编译,因为头文件<windows.h>定义了要映射到OpenFileAOpenFileW的标记UNICODE(取决于是否< =>是否已定义)。在定义函数之前,正确的解决方案是#undef <=>。

我只在#included文件中的宏干扰我的某个功能时使用它(例如,它具有相同的名称)。然后我#undef宏,所以我可以使用自己的功能。

虽然我认为Jonathan Leffler给了你正确的答案。这是一个非常罕见的情况,我使用#undef。通常,宏应该可以在许多函数中重用;这就是你在文件顶部或头文件中定义它的原因。但有时你在函数中有一些重复的代码,可以用宏缩短。


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;
}

为了向读者显示此宏仅在函数内部有用,最后它是未定义的。我不想鼓励任何人使用这种hackish宏。但如果你必须,最后#undef他们。

  

这是一种常见的做法来防范某人#defineing一个与你的功能同名的宏吗?或者这真的是一个不会在现实中发生的样本? (EG,没有人在他的权利,错误或疯狂的头脑中应该重写getchar(),所以它不应该出现。)

两者都有。好的代码不需要使用#undef,但是你必须使用很多不好的代码。当有人像#define bool int那样拉扯伎俩时,<=>可以证明是非常宝贵的。

除了修复宏污染全局命名空间的问题之外,另一个用途是 #undef 在这种情况下,宏可能需要在不同的地方有不同的行为。这并不是一个真正常见的场景,但我想到的几个场景是:

  • assert 如果您可能希望对代码的某些部分而不是其他部分执行调试,则可以在编译单元的中间更改宏的定义。此外 assert 本身需要成为 #undef想要做到这一点, NDEBUG 需要重新定义宏以重新配置所需的行为 assert

  • 我见过一种技术,用于通过使用宏将变量声明为来确保全局变量仅定义一次 extern, ,但是对于使用标头/声明来定义变量的单一情况,宏将被重新定义为空。

类似的东西(我并不是说这一定是一种好技术,只是我在野外见过的一种技术):

/* 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"

/* ------------------------------------------------------ */

如果可以定义宏,则必须有设施来取消。

我使用的内存跟踪器定义了自己的新/删除宏来跟踪文件/行信息。这个宏打破了SC ++ L。

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

关于更具体的问题:命名空间通常是emul;在C中通过为库函数添加标识符前缀。

盲目取消宏会增加混乱,降低可维护性,并可能破坏依赖于原始行为的事物。如果你被迫,至少使用push / pop来保持其他地方的原始行为。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top