#undefing in Practice?

Joh*_*udy 15 c c-preprocessor

我想知道#undef在C中的实际应用.我正在通过K&R工作,并且要接受预处理器.其中大部分是我(或多或少)理解的材料,但第90页(第二版)的内容突然出现在我面前:

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

#undef getchar

int getchar(void) { ... }

这是一种常见的做法来防御某人 - #define一个与你的功能同名的宏吗?或者这真的是一个不会在现实中发生的样本?(EG,没有人在他的权利,错误和疯狂的头脑中应该重写getchar(),所以它不应该出现.)有了你自己的功能名称,你觉得有必要这样做吗?如果您正在开发供其他人使用的库,这会改变吗?

Jon*_*ler 15

它能做什么

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

也就是说,做:

#include <stdio.h>
#undef getchar

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

int core_function(void)
{
   int c = some_function(getchar);
   return(c);
}
Run Code Online (Sandbox Code Playgroud)

正如所写,这core_function()是毫无意义的,但它说明了这一点.例如,您也可以使用isxxxx()宏执行相同的操作<ctype.h>.

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

很少需要

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

int c = (getchar)();
Run Code Online (Sandbox Code Playgroud)

因为后面的标记getchar不是(,它不是函数式宏的调用,所以它必须是对函数的引用.同样,上面的第一个例子,即使没有,也可以正确编译和运行#undef.

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

/* function.h */
…
extern int function(int c);
extern int other_function(int c, FILE *fp);
#define function(c) other_function(c, stdout);
…
Run Code Online (Sandbox Code Playgroud)
/* function.c */

…

/* Provide function despite macro override */
int (function)(int c)
{
    return function(c, stdout);
}
Run Code Online (Sandbox Code Playgroud)

函数定义行不会调用宏,因为后面的标记function不是(.该return行确实调用了宏.

  • 我刚刚偶然发现了一个奇怪的地方 - C 标准明确允许使用“#undef”来访问实际函数。然而,C++ 标准在 17.4.3.1.1 [lib.macro.names] 中有一个脚注,表示“不允许使用 #undef 指令删除库宏定义”。 (2认同)

Kon*_*lph 14

宏通常用于生成大量代码.它通常是一个非常本地化的用法,并且#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
Run Code Online (Sandbox Code Playgroud)


Ada*_*eld 6

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

  • 这里正确的解决方案是在Windows环境中不破坏windows功能. (2认同)