使用预处理器定义函数调用的缺点是什么?

发布于 2024-08-18 10:07:36 字数 168 浏览 4 评论 0原文

我想知道以这种方式使用预处理器的缺点是什么:

#define SOME_FUNCTION someFunction(someArgument)

基本上我觉得这是错误的(或者肯定不是最佳实践) - 但我不确定为什么......我的预处理器技能生锈了最好的。

I would like to know what the cons are of using the preprocessor in such a way:

#define SOME_FUNCTION someFunction(someArgument)

Basically I feel like this is wrong (or certainly not a best practice) - but I'm not sure why... my preprocessor skills are rusty at best.

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

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

发布评论

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

评论(9

仅此而已 2024-08-25 10:07:36

问题是每次使用参数时都会重新计算它们:

#define MIN(A,B)   ((A) < (B))?(A):(B);

请注意,我必须将所有参数包装在 '(' ')' 中以确保表达式正确计算。但如果我们这样做会发生什么呢?

int  s = MIN(++current,Max);

对此进行编码时,我希望电流在调用该函数之前增加一次。但因为它是一个宏,所以在测试中会增加一次,如果它仍然小于 Max,则会增加第二次

The problem is that the arguments are re=evaluated each time they are used:

#define MIN(A,B)   ((A) < (B))?(A):(B);

Notice that I have to wrap all the arguments in '(' ')' to make sure the expression evaluates corectly. But what happens if we do this?

int  s = MIN(++current,Max);

Coding this I would expect current to be incremented once before the function is called. But because it is a macro it is incremented once in the test and a second time if it is still smaller than Max

悲念泪 2024-08-25 10:07:36

缺点?通常宏定义不会出现在可执行文件的符号表中。调试起来稍微困难一些。

A downside? Usually a macro definition doesn't end up in the executable's symbol table. Slightly more difficult to debug.

白首有我共你 2024-08-25 10:07:36

您可以想到几个问题:

  • 在 C++ 中,宏没有命名空间和类范围,因此它在任何地方都是相同的。一个例子是 windows.h 中某处定义的不幸的 min 和 max。
    如果您正在为 Windows 编程并包含 windows.h 并想要编写 std::numeric_limits::max() 那么 max 将被一些代码替换...这会在预处理器运行后留下无法编译的代码。 (好吧,有一些方法可以关闭 windows.h 中的最小/最大宏,但它仍然是糟糕的设计!)
  • 该宏无法很好地调试。调试器将停止在使用宏的行上,而不是在宏内部的代码上...
  • 可能重新评估宏参数(您可以通过在宏内部使用带有局部变量的块来防止这种情况,但这会使调试变得更糟! )

There are several problems you can think of:

  • In C++ the macro has no namespace and class scope, so it is the same everywhere. An example for this are the unfortunate min and max defines somewhere in windows.h.
    If you are programming for windows and include windows.h and want to write std::numeric_limits::max() then max will be replaced by some code... This leaves uncompilable code after preprocessor run. (Okay there are ways to turn off min/max macros in windows.h but it's still poor design!)
  • The macro can't be debugged nicely. The debugger will stop on the line the macro is used not on the code inside the macro...
  • Possible reevaluation of the macro parameters (you could prevent this by having a block with local variables inside the macro but this would make debugging even worse!)
孤千羽 2024-08-25 10:07:36

好吧,如果你必须这样做(并且在某些情况下你可能会这么做),那么你至少应该将宏定义为“类似函数”:

#define SOME_FUNCTION() someFunction(defaultArgument)

否则你会编写看起来像常量赋值的代码。事实上是一个函数调用; IE;

x = SOME_FUNCTION ;  // hidden function call

但是对于“类似函数”的宏,您将不得不编写:

x = SOME_FUNCTION() ;  // shorthand function-call with default argument

哪个更好地匹配预处理器语法与语言语法。

一般来说,最好避免使用类似函数的宏,但有些宏比其他宏更阴险,这绝不是最坏的情况。但是,您可以轻松地用 C 编写函数包装器,或者在 C++ 中使用默认参数,这在大多数情况下会更可取。

Well if you must do that (and there are some occasions when you might), then you should at least define the macro as "function-like" thus:

#define SOME_FUNCTION() someFunction(defaultArgument)

otherwise you would write code that looked like an assignment by a constant when it was in fact a function call; i.e;

x = SOME_FUNCTION ;  // hidden function call

but with a "function-like" macro you would be obliged to write:

x = SOME_FUNCTION() ;  // shorthand function-call with default argument

Which better matches the pre-processor syntax with the language syntax.

Generally function-like macros are best avoided, but some are more insidious that others, this is by no means the worst case. However, you might just as easily write a function wrapper in C, or in C++ use a default argument, and this would be preferable in most cases.

疑心病 2024-08-25 10:07:36

在非常封闭的领域中,这有时很有用,可以以更易读的方式定义流程步骤:

void myfunc() {
  DO_STEP_ONE;
  THEN_ANOTHER_STEP;
  KEEP_GOING;
  LAST_STEP;
}

但通常它只会使代码更难以阅读和理解。

除非代码的读者值得详细了解这些 #define 的含义,并且这是某种捷径,否则您只是让人们查看两个地方来理解一行代码(文件顶部和文件中)函数)而不是一个。

我很少使用这种方法。

This is occasionally useful in a very closed domain for defining process steps in a more readable fashion:

void myfunc() {
  DO_STEP_ONE;
  THEN_ANOTHER_STEP;
  KEEP_GOING;
  LAST_STEP;
}

But usually it just makes code harder to read and understand.

Unless its worth the readers of your code getting to know what those #define's mean in detail, and it's some kind of short cut, you're just getting people to look in two places to understand a line of code (top of file and in function) rather than one.

I very rarely use this kind of approach.

鸠书 2024-08-25 10:07:36

这将取决于语言、编译器等,等等...

但是,这没有任何问题,正如名称所暗示的那样,这些指令发生在编译过程之前。

预处理器通过其实际值删除所有常量引用,具有正确代码的所有伪函数,等等......

That will depend on the language, compiler, etc, etc, etc...

However, there's nothing wrong with it, once as the name implies those directives occurs PREviously the PROCESS of compiling.

The pre-processor removes all constant references by its real value, all the pseudo-function with the proper code, and so on...

暗恋未遂 2024-08-25 10:07:36

计算机科学中的所有问题都可以
可以通过另一个层次来解决
间接

这是这些额外的间接级别之一:-)

假设在某个时候该函数需要另一个参数或者您根本不需要调用它,您可以更改 #define 一次而不是更改调用该函数的所有位置。

对于开发很有用,但保留在生产代码中很危险......一旦我有了成熟的代码并且知道我不需要更改它,我就会运行预处理器来替换该规则。

All problems in computer science can
be solved by another level of
indirection

This is one of these extra indirection levels :-)

Say that at some point that function needs another argument or you don't need to call it at all, you can change the #define once instead of changing all the places where the function is being called.

Useful for developing, but dangerous to keep in production code... I'd run the preprocessor to replace that rule once I have a mature code and know that I won't need to change it.

池木 2024-08-25 10:07:36

为什么要寻找使用特定预处理器代码的缺点?将预处理器宏用于除条件编译(包括包含防护)之外的任何内容都应该被自动视为不好的。正确的问题是特定用途是否有优势。

预处理器宏不以任何方式尊重范围或用法。他们可能会以意想不到的且难以找到的方式搞砸完美的代码。始终避免使用它们,除非您有充分的理由使用它们。

Why would you look for disadvantages in using specific preprocessor code? Using preprocessor macros for anything other than conditional compilation (including include guards) should be automatically considered bad. The correct question to ask is whether there are advantages to a particular use.

Preprocessor macros don't respect scope or usage in any way. They can screw up perfectly good code in unexpected and difficult-to-find ways. Always avoid them unless you've got a good reason to use one.

花开浅夏 2024-08-25 10:07:36

缺点是你隐藏了代码。
好处是你隐藏了代码。

负面影响通常超过正面影响。

通常这种特殊的方法几乎没有用处,除非调用看起来更像

someModule->someStorage->functionList[storage.getFunctionName].pointer->SomeFunction(...同样晦涩的参数...);

这样做是没有意义的。如果只有参数是一个晦涩的调用,则仅简写参数。如果只是函数,则仅简写函数。如果两者都是,那么您可能会更好

 SOME_FUNCTION(SOME_ARGUMENT);

如果该函数从未用其他任何东西调用,您可能会考虑将其从参数列表中删除并在函数体内获取。如果该对经常重复且变化很小,您可能会考虑包装函数。

当你在宏代码中犯了几个错误之后,你就会知道调试它们是很痛苦的,并且你不会轻率地使用它们。

The downside is that you are hiding the code.
The upside is that you are hiding the code.

The downside usually outweights the upside.

Usually this particular approach is pretty much useless and unless the call looks more like

someModule->someStorage->functionList[storage.getFunctionName].pointer->SomeFunction(...equally obscure argument...);

there is no point doing so. If only the argument is an obscure call, shorthand only the argument. If it's only the function, shorthand only the function. If both, you might be better off with

 SOME_FUNCTION(SOME_ARGUMENT);

If the function is never called with anything else, you might consider removing it from argument list and obtaining inside the function body. And if the pair repeats very often, in small variations, you might consider wrapper functions.

After you make several bugs in the code of a macro, you will learn it's pain to debug them and you won't use them frivolously.

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