如何使用预处理器删除全局数组?

时间:2016-01-08 03:40:37

标签: c c-preprocessor

我需要在寄存器GPIO0_SET_DATA23中设置一些不连续的位。我想将这些位索引为0,1,...

目前我使用全局数组来保存位位置,#define SET_LED(X)

以下是.c代码:

int led_bits[5] = {8, 9, 23, 24, 27};

以下是.h代码:

extern int led_bits[];
#define SET_LED(X) (GPIO0_SET_DATA23 = 1 << led_bits[X])

如何在预处理器中没有全局数组的情况下实现这一点?我只用常数整数调用SET_LED。我不会将其称为SET_LED(i)

4 个答案:

答案 0 :(得分:1)

在C99或更高版本中,您可以使用数组文字(和static inline函数):

static inline void SET_LED(int X)
{
    GPIO0_SET_DATA23 = 1 << (int [5]){8, 9, 23, 24, 27}[X];
}

这设置了一个全局变量 - 这有点难看。最好使用:

static inline int SET_LED(int X)
{
    return 1 << (int [5]){8, 9, 23, 24, 27}[X];
}

然后:

GPIO0_SET_DATA23 = SET_LED(4);

或者你需要的任何东西。这也允许您使用变量作为数字;使用#define LED0 8等不允许这样做(尽管你明确声明不需要变量支持)。将边界检查添加到内联函数可能是个好主意。 #define系统的一个优点是,如果您尝试SET_LED(6)但没有LED6宏,则代码将无法编译。

如果您正在使用没有C99或更高版本支持的嵌入式系统,则需要使用其他机制;这不适用于(大多数)C90编译器。

请注意,当您拥有优化编译器和函数的常量参数时,编译器将能够像您编写的那样计算值:

GPIO0_SET_DATA23 = 1 << 27;

当然,在编译时也将计算移位的值(如134,217,728 - 在逗号用作千位分隔符的语言环境中)。

答案 1 :(得分:0)

我不确定我是否完全理解你试图避免使用全局数组的原因,但你可以修改宏以使用指针和索引。

#define SET_LED(p, x) (GPIO0_SET_DATA23 = 1 << p[x])

然后,当你想调用SET_LED时,你可以将它传递给你的数组:

int led_bits[5] = {8, 9, 23, 24, 27};
SET_LED(led_bits, x);

这样您就可以避免在标题中使用extern int led_bits[];

答案 2 :(得分:0)

最后我找到了一种方法:

#define LED0 8
#define LED1 9
#define LED2 23
#define LED3 24
#define LED4 27

#define SET_LED(X) (GPIO0_SET_DATA23 = 1 << LED##X)

答案 3 :(得分:0)

这里不需要预处理器。一个普通的功能就可以了。

inline void
set_led(const int x)
{
  const int bits[] = {8, 9, 23, 24, 27};
  GPIO0_SET_DATA23 = (1U << bits[x]);
}

如果使用文字参数调用set_led,GCC会将其优化为预先计算的值的单个存储,因此与使用宏相比,没有开销。

为了证明,这是GCC在编译时生成的程序集

#define GPIO0_SET_DATA23 (*((volatile unsigned int *) 0xdeadbeef))

/* set_led as above */

int
test()
{
  set_led(3);
  return 0;
}

-O2

test:
.LFB1:
    .cfi_startproc
    movl    $3735928559, %eax
    movl    $16777216, (%rax)
    xorl    %eax, %eax
    ret
    .cfi_endproc