【问题标题】:Why is this C macro causing syntax error?为什么这个 C 宏会导致语法错误?
【发布时间】:2012-05-14 21:12:57
【问题描述】:

这是我第一次在 C 语言中使用宏,我试图用宏替换我通常放入函数中的大部分代码。这是一个经常使用的中断的一部分,因此我需要尽可能地优化它。看了文档,发现编译器不支持函数内联,想避免函数调用开销。

代码本身将数据发送到串行输入并行输出移位寄存器,据我所知,没有更短的方法可以编写我需要的代码。

我正在使用 C18 编译器版本 3.41 和 MPLAB X IDE。

这是我在函数形式中使用的代码:

void first_one(void)
{
   //3 invisible zeroes
            LATBbits.LATB1=0; //data set to zero

            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//third clock
            LATBbits.LATB0=0;
            //end of invisible zeroes

            //two visible zeroes    
            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;
            //end of two visible zeroes

            LATBbits.LATB1=1;//Data is now one

            LATBbits.LATB0=1;
            LATBbits.LATB0=0;
            //one 

            LATBbits.LATB1=0;//Data is now zero

            LATBbits.LATB0=1;//first clock
            LATBbits.LATB0=0;

            LATBbits.LATB0=1;//second clock
            LATBbits.LATB0=0;

            //after this, everything should be in place
            LATBbits.LATB0=1;
            LATBbits.LATB0=0;
}

我已经把函数变成了这个宏:

#define first_one() {  \
\
            LATBbits.LATB1=0;\               
                              \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
                                \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
                            \
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
            \                                
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
            \
            LATBbits.LATB1=1;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\    
\
            LATBbits.LATB1=0;\
             ^^^ The syntax error is here!
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
            LATBbits.LATB0=1;\
            LATBbits.LATB0=0;\
\
                     }

那我做错了什么?

更新:我删除了 cmets,现在在其他位置出现语法错误。

【问题讨论】:

  • 为什么你甚至认为你需要把函数变成一个宏?如果函数调用开销很大,只需将其设为内联函数即可。
  • @Paul R 正如我在问题中所说,编译器不支持函数内联。
  • ... 但正如 Paul 所说,你真的认为这里的函数调用开销很重要 - 毕竟这不是一个单行函数。对我来说看起来像 premature optimisation
  • @Clifford 是的,我很清楚“万恶之源”。是的,函数调用开销是个问题。在微控制器当前的工作频率下,我几乎没有足够的空间来足够快地为中断服务,而且微控制器应该做的其他事情也没有多少时间。是的,我考虑过增加频率,是的,这是一个大问题,所以最好不要增加频率。不,我无法转向更高效的架构。话虽如此,我相信我为优化这一点所做的努力是合理的。
  • @AndrejaKo:如果您无法满足实时截止日期,那么对这种微优化的需求表明您已经用尽了想法或能力。如果是后者,那么你的尝试也许是英勇的,但最终可能注定要失败。如果是前者,那么就有希望,但由于显而易见的原因,认识到这个问题很困难;如果一个解决方案是可能的,它将是在设计级别而不是编码级别。例如,除了 18 针 PIC18 部件外,所有部件都有一个 SPI 端口,可以在硬件中执行串行输出,从而减少代码空间 CPU 开销。

标签: c macros embedded pic c18


【解决方案1】:

检查\ 标记后没有空格,一些编译器会为此发出编译错误。

【讨论】:

  • 我检查了空格,确认没有,错误仍然存​​在。
  • 其实这就是问题所在。重新启动IDE几次后,看到程序编译正常,我尝试在``之后添加一个空格,并得到语法错误,就像我最初做的那样。
  • 这里有一个提示:在您的编辑器中选择/突出显示宏中的所有文本;任何尾随空格都将在违规行的末尾显示为彩色块。您甚至可以使用已发布问题中的文本来执行此操作,并查看问题出在哪里。
【解决方案2】:

在移除 cmets 之前将行拼接,因此 \//3 invisible zeroes 中的 \ 不会继续该行。

您需要删除注释或使用 C 样式注释 (/* 3 invisible zeroes */) 并将注释放在 之前的 \ 之前。

【讨论】:

  • 看起来主要问题不是 cmets。我删除了它们,但仍然出现语法错误。
  • 什么是语法错误?查看预处理的源代码并找出问题所在(在 gcc 和 Visual C++ 上使用 -E 编译)。
  • 语法错误是Error: syntax error,所以它不是很有帮助。 Visual Studio 和 GCC 不支持我现在编写代码的平台。
  • 无论您使用什么编译器,它都应该支持独立的预处理。请查阅其文档。
【解决方案3】:

问题在于 cmets 和预处理器处理它们的方式。删除 cmets,这应该可以正常工作。 或者使用 /* 注释 */

【讨论】:

    【解决方案4】:

    三个建议:

    首先,确保每个\ 之后没有任何尾随空格。

    其次,如果宏名称不包含任何参数,请从宏名称中删除 () edit 按以下 cmets 敲击。

    最后,将宏的内容包装在do {...} while(0) 中(没有尾随分号)。这样,当您在代码中编写 first_one(); 时,右大括号后将不会出现虚假分号。

    总之,

    #define do_first         \
      do {                   \
        LATBbits.LATB0 = 1;  \
        ...                  \
      } while(0)
    

    edit Lundin 指出这是过时且不必要的。我一直认为,如果宏扩展为{...}; 形式的语句,则需要避免诊断——显然我错了。不过,我仍然更喜欢它作为一种风格选择。

    【讨论】:

    • 我从不从应该像函数一样工作的宏中省略 (),即使它们不带参数。它使代码更清晰易读(这是一个类似函数的语句),而不仅仅是一个简单的符号。
    • 我同意,如果您将来获得标准的 C 编译器,请始终将 () 用于宏以使它们看起来像 C,并使它们与内联函数兼容。此外,do-while 建议是过时的。所有现代 C 编码标准都在每条语句后强制执行 {},以大幅减少错误数量。因此,如果您有一定的编码纪律,则无需执行此操作。
    • @Lundin:我该死。我可以 宣誓 {...}; 会引起胃灼热,但 gcc 可以很好地吞下它,即使使用 -pendatic 也是如此。我会接受保留() 的理由。我倾向于避免将宏用于符号常量之外的任何东西——只是太烧心了。
    • @JohnBode 你并不孤单,甚至 MISRA-C 委员会的 C 部门也有同样的信念,see this。但是,该规则已在 MISRA-C:2012 草案中删除。
    猜你喜欢
    • 2016-11-19
    • 2010-12-09
    • 2015-05-15
    • 1970-01-01
    • 2015-08-02
    • 1970-01-01
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多