【问题标题】:Unrecognized C macro无法识别的 C 宏
【发布时间】:2025-03-20 05:45:02
【问题描述】:

我遇到了一个在 C 头文件中定义的宏,我有点难以理解。

#if BAR
  #define FOO(s,err) \
          ((SOMEPOINTER)(s))->VALID != SOMEVARIABLE \
        ? (err) \
        :
#else
  #define FOO(s,err)

就是这样。我了解if/else 的情况,但我不确定FOO 的第一个宏定义在做什么。显然正在进行三元运算,但我对-> 很好奇,因为我在网上找不到对它的引用。我也很好奇这里似乎没有任何返回值。如果无论结果如何,您都没有返回任何东西,那么进行比较有什么意义?老实说,里面的“\”家伙也吓坏了我。

【问题讨论】:

  • 预处理器宏根本不返回值,-> 运算符让您可以访问struct 字段以获取struct poitners。
  • : 之后有什么遗漏吗??因为它看起来像一个搞砸的三元运算符。

标签: c macros multiline


【解决方案1】:

为了回答您的问题,

  1. ->是结构体指针解引用运算符,用于引用结构体类型指针的成员变量。

  2. \ 用于编写多行宏。参考C11 标准,第 §6.10.3 章,

参数由可选的标识符列表指定,其范围从标识符列表中的声明开始直到终止#define预处理指令的换行符

因此,要将 MACRO 的定义跨越多行,您需要使用 \

  1. #define 宏不返回任何值。它被视为预处理阶段的文本替换

【讨论】:

    【解决方案2】:
    if the name 'BAR' is defined to the pre processor of the compiler
    then
        define a macro 'FOO( s, err )'
        with the replacement text:
            "((SOMEPOINTER)(s))->VALID != SOMEVARIABLE ? (err) :"
            which is a ternary operator with a missing final parameter
            (so it would use what ever statement follows the macro invocation
    
    else, when the name 'BAR' is not known to the pre processor of the compiler
    then
        define the macro 'FOO( s, err )' as 'nothing
        (so what ever statement follows the macro invocation will always be executed, 
        rather than conditionally executed (as it would be above)
    

    【讨论】:

    • 所以,如果我们假设'SOMEPOINTER'是一个结构指针,'s'是一个与结构匹配的对象的句柄,'SOMEVARIABLE'持有一个要检查的值,这将转到如果 's' 的 VALID 部分与 'SOMEVARIABLE' 不匹配,则 "err",如果匹配则转到下一条语句?
    • 好吧,(err) 是宏的调用者所写的。那部分是常规的 C 三元语句。唯一真正的技巧是如果之前没有定义“BAR”。建议查看@Mints97 的答案
    【解决方案3】:

    要回答您的关于三元的问题,这可能是错误输入(忘记完成输入代码)的示例,或者是有人试图创建自己的(奇怪且违反直觉的)语法。宏可以这样使用:

    FOO(someVar, doThisIfSomeVarIsBad) doThisIfSomeVarIsGood(stuff);
    

    其中someVar 是一个指针(指向结构的指针或指向结构的指针转换为不同的指针类型),doThisIfSomeVarIsBad 是指向错误显示函数的函数指针,doThisIfSomeVarIsGood(stuff) 是任何声明。

    这是非常通用的(就像你的例子一样),但我希望你明白。

    这是一种有趣但仍然很奇怪的错误处理方式。但是,这并不是那么糟糕,我见过更糟糕的预处理器滥用......

    要回答您的其他问题

    ((SOMEPOINTER)(s))->VALID 表示您将s 转换为指向结构的类型并访问其成员(-> 表示通过指向结构的指针访问struct 成员)。

    \,出现在换行符之前,“转义”换行符。这是必要的,因为#define-s 应该(正式)是单行的,所以如果你要跨行拆分宏,你必须让编译器认为它仍然是一行。

    是的,类似函数的宏不同于内联函数,它们不返回任何东西。它们是简单的文本替换工具,它们只是用一些不同的代码替换一些代码。

    【讨论】:

    • 我希望不是那样的。
    • @PeterM:哈,我也是,但我认为很明显这个宏就是这样使用的。恕我直言,它不能在其他任何地方工作
    • 最重要的是,这段代码隐藏在一系列指向其他宏的宏的末尾...谈论深入研究