首页 > 解决方案 > 带有扩展宏和类型转换的 C 条件预处理器

问题描述

对于微控制器,我将宏用于 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 是具有类型转换(例如(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

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

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

标签: cc-preprocessorpreprocessor

解决方案


引用C11,第§6.10.1p4章

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

在您的代码中,您正在比较 -

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

因为double不是一个有效的令牌,它被替换为(0).

本质上你是在比较 -

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

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

当我用我的编译器运行它时clang,我得到

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

您提到的解决方案似乎是一个不错的解决方案。您不必担心“但它涉及另一个变量”,因为这些不是变量而是宏。宏是编译时实体,不会以任何方式(内存、寄存器压力甚至执行时间)给您的运行时间带来负担。


推荐阅读