【问题标题】:C macro compiler warningsC 宏编译器警告
【发布时间】:2019-03-15 04:53:47
【问题描述】:

我定义了一个宏,使用来自我之前问过here 的问题的输入。该宏旨在设置、清除或检查 GPIO 引脚状态。 宏按预期工作,但编译时出现问题。我在任何使用它的地方都会收到编译器警告:

警告逗号表达式的右手操作数无效

当我像这样使用宏时:

#define ON  1
#define OFF 2
#define ENA 3
#define OUT_3(x) (x==ON) ? (PORTJ.OUTSET=PIN2_bm) : (x==OFF) ? (PORTJ.OUTCLR=PIN2_bm) : (x==ENA) ? (PORTJ.DIRSET=PIN2_bm) : (PORTJ.DIRCLR=PIN2_bm)
#include <avr/io.h>

if (something) OUT_3(ENA);

但是,如果我这样做:

if (something) {OUT_3(ENA);}

我不再收到警告。

为什么会有差异?我应该如何更改宏来防止这种情况发生?

另外,这会引发警告:

int i=0;
if (something) i=1, OUT_3(ENA);

但事实并非如此:

int i=0;
if (something) OUT_3(ENA), i=1;

我对逗号分隔表达式的理解显然有点偏离。编译器如何看到这个?我查看了其他几个与此类似的问题,但仍然不完全理解其中的区别。

【问题讨论】:

  • 在宏的任何地方都看不到逗号
  • 无法重现。请发帖minimal reproducible example
  • 括号是你的朋友。使用它们。
  • 警告来自其他地方。
  • 使用gcc -E查看宏的完整展开。

标签: c avr avr-gcc


【解决方案1】:

这个宏很讨厌有几个原因:

  1. 宏参数应始终用括号括起来,以避免潜在的运算符优先级问题。将(x==ON) 更改为((x)==ON)
  2. 嵌套的三元运算应该用括号括起来,以使执行顺序清晰。将a ? b : c ? d : e 更改为a ? b : (c ? d : e)
  3. 完整的宏应该用括号#define MACRO (...) 或do-while-zero 循环#define MACRO do {...} while(0) 括起来,以避免可能出现的运算符优先级问题。更多内容如下。
  4. 三元运算符在这里并不是很有用,因为您没有使用它的返回值。您应该使用常规的 if 或 switch 语句。这就是前面提到的 do-while-zero 循环变得方便的地方:

    #define OUT_3(x) \
        do { \
            if((x) == ON)       { PORTJ.OUTSET = PIN2_bm; } \
            else if((x) == OFF) { PORTJ.OUTCLR = PIN2_bm; } \
            else if((x) == ENA) { PORTJ.DIRSET = PIN2_bm; } \
            else                { PORTJ.DIRCLR = PIN2_bm; } \
        } while(0)
    
  5. 但是宏真的有必要吗?您可以改用内联函数,并摆脱所有宏的怪异:

    static inline void OUT_3(x_type x) {
        if(x == ON)       { PORTJ.OUTSET = PIN2_bm; }
        else if(x == OFF) { PORTJ.OUTCLR = PIN2_bm; }
        else if(x == ENA) { PORTJ.DIRSET = PIN2_bm; }
        else              { PORTJ.DIRCLR = PIN2_bm; }
    }
    

通过这些更改,您的错误可能会消失,并且您的代码更易于阅读。

【讨论】:

  • 宏很讨厌,因为它们看起来像句法单元,但实际上是标记序列。 OUT_3(x) 看起来像一个函数,但会产生各种奇怪的副作用,包括对周围代码的影响。甚至上面的建议 4 也是一个陷阱。 if x 是一个有副作用的表达式 x 被评估 1 到 3 次,但在提案 5 中只有一次。你似乎在这里传递了一个值 (ENA) 但是如果你有一天扩散这种风格由于隐藏在一堆宏中的副作用,您会引发一些奇怪的错误。直到你被一个奇怪的宏观副作用“感染”后,你才赢得了 C 马刺。
  • 这很可能就是我正在经历的。那么我是否有理由假设这不是宏的地方并切换到使用函数来实现我的目标?
  • @hvguy 就我所见,使用宏似乎没有任何好处。在这些情况下,默认使用常规函数会更安全。
  • @user694733 谢谢。我将重新评估我的宏。我通常会尝试将这些基本的引脚操作功能保留为宏,但如果它导致问题,那么没有理由不切换。
【解决方案2】:

我无法重现您的问题。这段代码没有任何问题

main.cpp

#include <stdio.h>
enum type{ON, OFF, ENA};

#define OUT_3(x) (x==ON) ? (printf("ON\n")) : (x==OFF) ? (printf("OFF\n")) : (x==ENA) ? (printf("ENA\n")) : (printf("OTHER\n"))

int main(){
  int a = 2;
  if(a == 1) OUT_3(ON);
  if(a == 2) OUT_3(OFF);
  if(a == 3) OUT_3(ENA);

  return 0;
}

编译

gcc -Wall -O0 -g -o main main.c

您能否展示一下 ON、OFF、ENA 和端口定义的定义?

【讨论】:

  • 已编辑以显示更多信息。端口定义是 avr xmega 包含的标准,可以用其他标准替换,同时仍然产生类似的结果。
  • 你仍然缺少PIN2_bmPORTJ.DIRCLRPORTJ.DIRSET等的定义
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多