实践中的#undef-ing?

发布于 2024-07-06 18:36:13 字数 517 浏览 6 评论 0原文

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

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

#undef getchar

int getchar(void) { ... }

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

I'm wondering about the practical use of #undef in C. I'm working through K&R, and am up to the preprocessor. Most of this was material I (more or less) understood, but something on page 90 (second edition) stuck out at me:

Names may be undefined with #undef,
usually to ensure that a routine is
really a function, not a macro:

#undef getchar

int getchar(void) { ... }

Is this a common practice to defend against someone #define-ing a macro with the same name as your function? Or is this really more of a sample that wouldn't occur in reality? (EG, no one in his right, wrong nor insane mind should be rewriting getchar(), so it shouldn't come up.) With your own function names, do you feel the need to do this? Does that change if you're developing a library for others to use?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

太阳公公是暖光 2024-07-13 18:36:13

它的作用

如果您阅读 Plauger 的 标准 C 库 (1992),您将看到 标头允许提供 getchar()getc() 作为类似函数的宏(具有 getchar()getc() 的特殊权限code>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);
}

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

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

很少需要

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

int c = (getchar)();

因为 getchar 之后的标记> 不是 (,它不是类函数宏的调用,因此它必须是对该函数的引用。类似地,上面的第一个示例即使没有#undef

如果您使用宏覆盖来实现自己的函数,则可以使用它来达到良好的效果,尽管除非进行解释,否则可能会有点混乱。

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

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

What it does

If you read Plauger's The Standard C Library (1992), you will see that the <stdio.h> header is allowed to provide getchar() and getc() as function-like macros (with special permission for getc() to evaluate its file pointer argument more than once!). However, even if it provides macros, the implementation is also obliged to provid actual functions that do the same job, primarily so that you can access a function pointer called getchar() or getc() and pass that to other functions.

That is, by doing:

#include <stdio.h>
#undef getchar

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

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

As written, the core_function() is pretty meaningless, but it illustrates the point. You can do the same thing with the isxxxx() macros in <ctype.h> too, for example.

Normally, you don't want to do that - you don't normally want to remove the macro definition. But, when you need the real function, you can get hold of it. People who provide libraries can emulate the functionality of the standard C library to good effect.

Seldom needed

Also note that one of the reasons you seldom need to use the explicit #undef is because you can invoke the function instead of the macro by writing:

int c = (getchar)();

Because the token after getchar is not an (, it is not an invocation of the function-like macro, so it must be a reference to the function. Similarly, the first example above, would compile and run correctly even without the #undef.

If you implement your own function with a macro override, you can use this to good effect, though it might be slightly confusing unless explained.

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

The function definition line doesn't invoke the macro because the token after function is not (. The return line does invoke the macro.

千纸鹤 2024-07-13 18:36: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

Macros are often used to generate bulk of code. It's often a pretty localized usage and it's safe to #undef any helper macros at the end of the particular header in order to avoid name clashes so only the actual generated code gets imported elsewhere and the macros used to generate the code don't.

/Edit: As an example, I've used this to generate structs for me. The following is an excerpt from an actual project:

#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
一抹微笑 2024-07-13 18:36:13

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

Because preprocessor #defines are all in one global namespace, it's easy for namespace conflicts to result, especially when using third-party libraries. For example, if you wanted to create a function named OpenFile, it might not compile correctly, because the header file <windows.h> defines the token OpenFile to map to either OpenFileA or OpenFileW (depending on if UNICODE is defined or not). The correct solution is to #undef OpenFile before defining your function.

呆° 2024-07-13 18:36:13

尽管我认为乔纳森·莱夫勒给了你正确的答案。 这是一个非常罕见的情况,我使用#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;
}

为了向读者表明该宏仅在函数内部有用,它在末尾是未定义的。 我不想鼓励任何人使用这样的黑客宏。 但如果必须的话,请在最后 #undef 它们。

Although I think Jonathan Leffler gave you the right answer. Here is a very rare case, where I use an #undef. Normally a macro should be reusable inside many functions; that's why you define it at the top of a file or in a header file. But sometimes you have some repetitive code inside a function that can be shortened with a macro.


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

To show the reader that this macro is only useful inside of the function, it is undefined at the end. I don't want to encourage anyone to use such hackish macros. But if you have to, #undef them at the end.

孤凫 2024-07-13 18:36:13

这是防御某人 #define-ing 与您的函数同名的宏的常见做法吗? 或者这实际上更像是现实中不会发生的样本? (EG,任何人无论是对的、错的还是疯狂的头脑都不应该重写 getchar(),所以它不应该出现。)

两者都有一点。 好的代码不需要使用#undef,但是您必须使用很多坏的代码。 当有人使用像 #define bool int 这样的技巧时,#undef 可以证明是无价的。

Is this a common practice to defend against someone #define-ing a macro with the same name as your function? Or is this really more of a sample that wouldn't occur in reality? (EG, no one in his right, wrong nor insane mind should be rewriting getchar(), so it shouldn't come up.)

A little of both. Good code will not require use of #undef, but there's lots of bad code out there you have to work with. #undef can prove invaluable when somebody pulls a trick like #define bool int.

少钕鈤記 2024-07-13 18:36:13

我仅在#included 文件中的宏干扰我的函数之一时使用它(例如,它具有相同的名称)。 然后我#undef宏,这样我就可以使用我自己的函数。

I only use it when a macro in an #included file is interfering with one of my functions (e.g., it has the same name). Then I #undef the macro so I can use my own function.

独守阴晴ぅ圆缺 2024-07-13 18:36:13

除了修复宏污染全局命名空间的问题之外,#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"

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

In addition to fixing problems with macros polluting the global namespace, another use of #undef is the situation where a macro might be required to have a different behavior in different places. This is not a realy common scenario, but a couple that come to mind are:

  • the assert macro can have it's definition changed in the middle of a compilation unit for the case where you might want to perform debugging on some portion of your code but not others. In addition to assert itself needing to be #undef'ed to do this, the NDEBUG macro needs to be redefined to reconfigure the desired behavior of assert

  • I've seen a technique used to ensure that globals are defined exactly once by using a macro to declare the variables as extern, but the macro would be redefined to nothing for the single case where the header/declarations are used to define the variables.

Something like (I'm not saying this is necessarily a good technique, just one I've seen in the wild):

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

/* ------------------------------------------------------ */
转身泪倾城 2024-07-13 18:36:13

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

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

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

关于您更具体的问题:命名空间通常在 C 中通过在库函数前添加标识符来模拟。

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

If a macro can be def'ed, there must be a facility to undef.

a memory tracker I use defines its own new/delete macros to track file/line information. this macro breaks the SC++L.

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

Regarding your more specific question: namespaces are often emul;ated in C by prefixing library functions with an identifier.

Blindly undefing macros is going to add confusion, reduce maintainability, and may break things that rely on the original behavior. If you were forced, at least use push/pop to preserve the original behavior everywhere else.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文