C - 为结构数组创建初始化宏

时间:2017-11-01 14:42:56

标签: c c-preprocessor array-initialization

我想用宏来做这件事:

typedef struct _TIMER_llist {
    struct _TIMER_llist *next;
    uint32_t time;
    const int id;
} TIMER_llist;

TIMER_llist _timer_llists[] =
{
    { .id = 1, .next = &_timer_llists[1] },
    { .id = 2, .next = &_timer_llists[2] },
    { .id = 3, .next = &_timer_llists[3] },
    { .id = 4, .next = &_timer_llists[4] },
    { .id = 5, .next = &_timer_llists[5] },
    { .id = 6, .next = &_timer_llists[6] },
    { .id = 7, .next = &_timer_llists[7] },
    { .id = 8, .next = &_timer_llists[8] },
    { .id = 9, .next = &_timer_llists[9] },
    { .id = 10, .next = &_timer_llists[10] },
    { .id = 11, .next = &_timer_llists[11] },
    { .id = 12, .next = &_timer_llists[12] },
    { .id = 13, .next = 0 } };

//This won't work, because const .id
TIMER_llist _timer_llists[1];
void init() {
    _timer_llists[0].id = 1;
    _timer_llists[0].next = 0;
}

我没有为每个缓冲区条目写一行,而是想使用

#define NUMBER_ENTRIES 13  

因为这真的很不方便,如果我想做64个条目......

不幸的是,我没有C预处理器的经验,所以我很好奇:

  1. 有一个更好的非宏解决方案
  2. 使用gcc轻松实现此宏操作
  3. 此外,这更像是理论方面,是执行速度。虽然第一篇文章中的例子只是一个memcpy(.data [?],_ timer_llists,sizeof(_timer_llists)),而一个程序化的方法至少需要一个计数器,可能是最后一个案例的if语句,以及一个函数开销(好吧,也许是内联)。另一方面,这将节省.data中的空间。

    在这个特殊情况下,我使用的是gcc-avr,但这个问题反复出现在我的脑海中,我希望有一个通用的方法。

3 个答案:

答案 0 :(得分:3)

如果你使用boost预处理器(参见n.m。的评论),你可以这样做:

#include <boost/preprocessor/enum.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/arithmetic/dec.hpp>

#define TIMER_ENTRY(_, n, data) \
   { .id = BOOST_PP_INC(n), .next = &data[BOOST_PP_INC(n)] }
#define DECL_TIMER_LLIST(NAME_, COUNT_) \
   TIMER_llist NAME_[] = \
   { \
      BOOST_PP_ENUM(BOOST_PP_DEC(COUNT_), TIMER_ENTRY, NAME_) \
      , { .id = COUNT_, .next = 0 } \
   }

BOOST_PP_ENUM几乎是你需要的一切。

BOOST_PP_INCBOOST_PP_DEC补充了特殊的开始案例(1开始计数而不是默认0)/结束案例(最多计数n-1添加不同的行。)

Here's a "live" sample.

(仅供参考,除非您在时间舱中找到硬件上的编程,否则在比较方法时我甚至不会考虑编程方案的开销)。

答案 1 :(得分:1)

如果你真的需要进行初始化而不是以编程方式进行初始化,那么使用宏可以做到最好:

#define TIMERINIT(a)      {.id = a, .next = &_timer_llists[a] },
#define END_TIMERINIT(a)  {.id = a, .next = 0 },

TIMER_llist _timer_llists[] =
{
  TIMERINIT(1)
  TIMERINIT(2)
  TIMERINIT(3)
  TIMERINIT(4)
  ...
  TIMERINIT(12)
  END_TIMERINIT(13)
};

它并不完美,但仍然比手工编写所有内容更好。

现在的程序化解决方案(这是最简单,最灵活的解决方案IMO):

#define NUMBER_ENTRIES 13
TIMER_llist _timer_llists[NUMBER_ENTRIES];

int main()
{
  for (int i = 0; i < NUMBER_ENTRIES; i++)
  {
    _timer_llists[i].id = i + 1;
    _timer_llists[i].next = &_timer_llists[i + 1];
  }
  ...
}

对于大于约8的NUMBER_ENTRIES,使用此解决方案生成的代码很可能会更短。

另一种方法是编写一个特殊程序,通过将其写入.h文件并将该.h文件包含到.c文件中来生成初始化表。然后,您的构建过程应该在comnpilation之前调用该特殊程序。

答案 2 :(得分:1)

您可以使用“X-macros”。它不一定是硬编码所有内容的改进,但它看起来像这样:

#define TIMER_INIT_LIST \
  X(1)   \
  X(2)   \
  X(3)   \
  X(4)   \
  X(5)   \
  X(6)   \
  X(7)   \
  X(8)   \
  X(9)   \
  X(10)  \
  X(11)  \
  X(12)  \
  X(13)  \

int main()
{
  TIMER_llist _timer_llists[] =
  {
    #define X(n) [n-1] = {.id = n, .next = &_timer_llists[n] },
      TIMER_INIT_LIST
    #undef X
  };

  return 0;
} 

同时,我通过使用数组索引指定的初始化程序改进了代码,以确保数据完整性,即使X宏列表被修改或未排序。

宏扩展为这样的东西:

[0] = {.id = 1, .next = .next = &_timer_llists[1] },
[1] = {.id = 2, .next = .next = &_timer_llists[2] },
...