C预处理器以扩展宏和类型转换为条件

时间:2018-05-18 10:18:31

标签: c c-preprocessor preprocessor

对于微控制器,我使用宏来进行HAL。现在,为了概括HAL的使用,我想做一些像

这样的事情
#define UART  UART1
#if UART==UART1
# define PIN_TX 9
#elif UART==UART2
# define PIN_TX 2
#else
# warning "UART not correctly defined"
#endif

但是,UART1是一个带有类型转换的内存地址(e.x. (uint8_t*)0x004000000)。所以编译器会输出一些错误。

我做了一个简单的例子:

#include <stdio.h>

#define v1 (double)1
#define v2 (double)2

int main(int argc, char *argv[])
{
  printf("We have: ");

#define VAL (v1)

#if VAL==v1
  printf("VAL is 1\n");
#elif VAL==v2
  printf("VAL is 2\n");
#else
# warning "VAL not 1 or 2"
  printf("Not defined\n");
#endif
}

还无法使用gcc编译以下注释:

cc     preproc.c   -o preproc
preproc.c: In function ‘main’:
preproc.c:3:20: error: missing binary operator before token "1"
 #define v1 (double)1
                    ^
preproc.c:10:14: note: in expansion of macro ‘v1’
 #define VAL (v1)
              ^~
preproc.c:12:5: note: in expansion of macro ‘VAL’
 #if VAL==v1
     ^~~
preproc.c:3:20: error: missing binary operator before token "1"
 #define v1 (double)1
                    ^
preproc.c:10:14: note: in expansion of macro ‘v1’
 #define VAL (v1)
              ^~
preproc.c:14:7: note: in expansion of macro ‘VAL’
 #elif VAL==v2
       ^~~
preproc.c:17:2: warning: #warning "VAL not 1 or 2" [-Wcpp]
 #warning "VAL not 1 or 2"
  ^~~~~~~
<builtin>: recipe for target 'preproc' failed
make: *** [preproc] Error 1

但是,如果我删除了v1和v2定义中的(double),它会按预期编译并运行。

请注意,作为替代解决方案我做了

#define USE_UART1
//#define USE_UART2

#if defined(USE_UART1)
# define UART UART1
# define PIN_TX 9
#elif defined(USE_UART2)
# define UART UART2
# define PIN_TX 2
#else
# warning "UART not correctly defined"
#endif

但这涉及另一个变量[编辑:技术上是一个宏,但实际上是另一堆我必须跟踪的字符]。

我很想知道编译错误背后的原理和/或如果可能的话如何解决它。

2 个答案:

答案 0 :(得分:4)

引用C11,章§6.10.1p4

  

在评估之前,将替换将成为控制常量表达式的预处理标记列表中的宏调用(除了由定义的一元运算符修改的那些宏名称之外),就像在普通文本中一样。如果定义的令牌是由于此替换过程而生成的,或者在宏替换之前使用定义的一元运算符与两个指定表单中的一个不匹配,则行为是未定义的。 在执行了由于宏扩展和已定义的一元运算符导致的所有替换后,所有剩余的标识符(包括与关键字词法相同的标识符)将替换为pp-number 0 ,然后转换每个预处理标记进入一个令牌。 ...

在您的代码中进行比较 -

#if ((double)1)==((double)1)

由于double不是有效令牌,因此会被(0)替换。

基本上你在比较 -

#if ((0)1)==((0)1)

由于语法错误,这不是有效的常量表达式。

当我使用编译器clang运行时,我得到了

  

错误:令牌不是预处理器子表达式中的有效二元运算符

你提到的解决方案似乎很好。你不应该担心&#34;但它涉及另一个变量&#34;因为这些不是变量而是宏。宏是编译时实体,不会以任何方式减少运行时间(内存,寄存器压力甚至执行时间)。

答案 1 :(得分:2)

你工作替代方案的一些方法是解决诸如此类问题的常见方法。另一种方法是使用某种配置程序将适当的宏定义写入头文件。

  

但这涉及另一个变量[...]

不,根本没有涉及变量。预处理器宏不是变量,它们是。它们的行为不像变量,除了少数肤浅的方式。否则就是导致你开始走错路的原因。宏表示源代码块,而变量表示正在运行的程序中的存储位置。