do { } while(0) 替代宏定义或任何其他可能的选项 [duplicate]

do { } while(0) alternative in macro definition or any other possible options [duplicate]

提问人:filberol 提问时间:11/9/2023 最后编辑:Peter Mortensenfilberol 更新时间:11/10/2023 访问量:144

问:

我遇到了一种在 C/C++ 编译中制作多行宏函数定义的方法,它不符合任何明确的代码约定(并且可能会产生性能缺陷?这是实现制作宏函数目标的唯一途径吗?开发人员会引入任何其他方法来做到这一点吗?

我看到这不是关于while(0)的第一个问题,但我正在寻找任何可能的替代方案,也许我应该只编写普通函数。

我可以看到这种技术在 Linux 内核中使用,因此我可以说这样写是合法的吗?Linux 存储库搜索

C 定义 约定

评论

6赞 interjay 11/9/2023
你为什么想要替代品?有什么问题?它绝对没有“性能缺点”。do ... while (0)
3赞 HolyBlackCat 11/9/2023
“也许我应该只写普通函数”是的,你应该编写普通函数。宏应该是最后的手段。
4赞 William Pursell 11/9/2023
“不符合任何明确的代码约定”是什么意思?在宏中使用的做法已经存在了很长时间,并且非常传统。这是一个非常常见的成语。do {} while(0)
4赞 Lundin 11/9/2023
查看宏中的 { } while(0) 是什么,我们应该使用它吗?如前所述,无论您是在编写未知数使用的库,还是只是一些最终应用程序,这都很重要。不过,一般来说,宏始终是最后的手段,如果您可以使用函数,请使用函数。
2赞 Lundin 11/9/2023
“我可以看到这种技术被用于 Linux 内核” Linux 内核实际上与规范的 C 代码相去甚远。

答:

2赞 klutt 11/9/2023 #1

宏和清晰的代码一开始就不能很好地混合。基本上就是这样。

是的,这是一个有点笨拙的解决方法。然而,它已成为如何做你想做的事的事实标准。如果你找到其他一些聪明的替代方案,你可能会使代码变得不那么清晰,因为 C 程序员认为这是既定的约定。do ... while(0)do ... while(0)

不要担心性能。任何编译器都可以看到它只会执行一次,并且会优化循环。do ... while(0)

根据这个答案,这是做你想做的事情的唯一方法。我不知道这在技术上是否正确,但我很确定没有好的方法可以解决这个问题。至少不是在所有情况下。在某些情况下,可以使用逗号运算符,例如,但这不适用于更复杂的情况。但是,在某些情况下,您将不得不使用这种方法。例如,如果您使用 do-while 将给出错误,但逗号和 do-while 都不起作用。你可以让它使用 ,但你仍然会被限制在非常简单的结构上。#define FOO(x) a(x), b(x)if(FOO(x))a = FOO(x)#define FOO(x) (a(x), b(x))

我尽量远离宏。它们往往会使调试变得更加困难。

1赞 Aconcagua 11/9/2023 #2

首先:宏通常更难阅读,并且经常使调试更加复杂——如果适用,如果具体用例允许,应该首选函数或其他替代方案。

如果无论出于何种原因都无法避免宏,那么实际上只需将多个表达式包装在一对大括号中即可。但是,该方法强制在之后放置一个分号,这对于生成干净的代码是可取的(宏的使用看起来像函数调用或其他类型的单个表达式)并防止使用函数外部(尽管宏包装的代码通常也会这样做)。因此,仅仅包裹在大括号中显然不如这种方法。do { } while(0)do { } while(0)

实际上,似乎还有另一种选择:

#define FOO if(1) { /* multiple expressions */ } else ((void)0)

克里斯·克莱恩(Chris Kline)在对类似问题的回答的评论中强调了在宏参数本身包含(复杂)代码的情况下,在安全性方面优于该方法。但是,编写此类宏可能被视为代码,不应应用;相比之下,此变体会静默地丢弃后续表达式,例如,如果通过序列(逗号)运算符附加,例如:do { } while(0)

FOO, bar();
//   ^ unexpectedly won't get called!

所以不能保证安全使用!

评论

1赞 Lundin 11/9/2023
他的评论是无稽之谈,任何写出可憎的东西都应该得到宏观问题。MYMACRO( SomeFunc(i)==true, {break;} )
1赞 HolyBlackCat 11/9/2023
这是一个糟糕的选择,因为(或 等)静默地跳过调用,而会给你一个编译错误。FOO, bar();FOO + bar()bar()do{ }while(0)
0赞 HolyBlackCat 11/9/2023
我的意思是(不记得是我写的还是最初写的)。static_assert(true)truefalse
0赞 Aconcagua 11/9/2023
@HolyBlackCat嗯......事实上,这似乎可以解决问题。但是这个问题被标记为 C,所以对我来说似乎不合适,假设我不会添加。
0赞 tstanisl 11/9/2023
考虑更简洁的版本:.它也会对尾巴表达有抵抗力。if(0);else{ ... }