实践中的#undef-ing?
我想知道 #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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
它的作用
如果您阅读 Plauger 的 标准 C 库 (1992),您将看到
标头允许提供getchar()
和getc()
作为类似函数的宏(具有getchar()
和getc()
的特殊权限code>getc() 多次评估其文件指针参数!)。 然而,即使它提供了宏,实现也有义务提供执行相同工作的实际函数,主要是为了您可以访问名为getchar()
或getc() 的函数指针
并将其传递给其他函数。也就是说,通过执行以下操作:
正如所写,
core_function()
毫无意义,但它说明了这一点。 例如,您也可以使用
中的isxxxx()
宏执行相同的操作。通常,您不想这样做 - 您通常不想删除宏定义。 但是,当您需要真正的功能时,您可以找到它。 提供库的人可以模拟标准C库的功能,效果很好。
很少需要
另请注意,您很少需要使用显式
#undef
的原因之一是因为您可以通过编写调用函数而不是宏:因为
getchar
之后的标记> 不是(
,它不是类函数宏的调用,因此它必须是对该函数的引用。类似地,上面的第一个示例即使没有#undef
。如果您使用宏覆盖来实现自己的函数,则可以使用它来达到良好的效果,尽管除非进行解释,否则可能会有点混乱。
函数定义行不会调用宏,因为
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 providegetchar()
andgetc()
as function-like macros (with special permission forgetc()
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 calledgetchar()
orgetc()
and pass that to other functions.That is, by doing:
As written, the
core_function()
is pretty meaningless, but it illustrates the point. You can do the same thing with theisxxxx()
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: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.
The function definition line doesn't invoke the macro because the token after
function
is not(
. Thereturn
line does invoke the macro.宏通常用于生成大量代码。 它通常是一种相当本地化的用法,并且可以安全地在特定标头末尾使用
#undef
任何辅助宏,以避免名称冲突,因此只有实际生成的代码会在其他地方导入,并且用于生成的宏代码没有。/编辑:作为一个例子,我用它来为我生成结构。 以下是实际项目中的摘录:
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 都位于一个全局命名空间中,因此很容易导致命名空间冲突,尤其是在使用第三方库时。 例如,如果您想创建一个名为
OpenFile
的函数,它可能无法正确编译,因为头文件
定义了标记OpenFile
映射到OpenFileA
或OpenFileW
(取决于是否定义了UNICODE
)。 正确的解决方案是在定义函数之前使用#undef
OpenFile
。Because preprocessor
#define
s 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 namedOpenFile
, it might not compile correctly, because the header file<windows.h>
defines the tokenOpenFile
to map to eitherOpenFileA
orOpenFileW
(depending on ifUNICODE
is defined or not). The correct solution is to#undef
OpenFile
before defining your function.尽管我认为乔纳森·莱夫勒给了你正确的答案。 这是一个非常罕见的情况,我使用#undef。 通常,宏应该可以在许多函数中重用; 这就是为什么您将其定义在文件顶部或头文件中。 但有时函数内有一些重复的代码,可以使用宏来缩短。
为了向读者表明该宏仅在函数内部有用,它在末尾是未定义的。 我不想鼓励任何人使用这样的黑客宏。 但如果必须的话,请在最后 #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.
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.
两者都有一点。 好的代码不需要使用
#undef
,但是您必须使用很多坏的代码。 当有人使用像#define bool int
这样的技巧时,#undef
可以证明是无价的。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
.我仅在
#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.除了修复宏污染全局命名空间的问题之外,#undef 的另一个用途是可能需要宏在不同位置具有不同行为的情况。 这并不是一个真正常见的场景,但我想到的一些情况是:
assert 宏可以在编译单元的中间更改其定义,以适应您可能想要的情况对代码的某些部分进行调试,但不对其他部分进行调试。 除了需要对
assert
本身进行#undef
编辑来执行此操作之外,还需要重新定义NDEBUG
宏来重新配置所需的行为assert
我见过一种技术,用于通过使用宏将变量声明为
extern
来确保全局变量只定义一次,但宏将被重新定义对于使用标头/声明来定义变量的单一情况,什么都没有。类似的东西(我并不是说这一定是一种好技术,只是我在野外见过的一种技术):
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 toassert
itself needing to be#undef
'ed to do this, theNDEBUG
macro needs to be redefined to reconfigure the desired behavior ofassert
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):
如果可以定义宏,则必须有取消定义的工具。
我使用的内存跟踪器定义了自己的新/删除宏来跟踪文件/行信息。 这个宏破坏了 SC++L。
关于您更具体的问题:命名空间通常在 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.
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.