C预处理器中指向void的指针

时间:2019-06-15 11:22:36

标签: c gcc goto pointer-arithmetic

我阅读了这份资料(https://github.com/lattera/glibc/blob/master/stdio-common/vfprintf.c),并发现了一些有趣的词句,但我并未完全理解:

#ifdef SHARED
/* 'int' is enough and it saves some space on 64 bit systems.  */
# define JUMP_TABLE_TYPE const int
# define JUMP_TABLE_BASE_LABEL do_form_unknown
# define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL
# define JUMP(ChExpr, table)                              \
  do                                      \
{                                     \
  int offset;                                 \
  void *ptr;                                  \
  spec = (ChExpr);                            \
  offset = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown)          \
    : table[CHAR_CLASS (spec)];                       \
  ptr = &&JUMP_TABLE_BASE_LABEL + offset;                 \
  goto *ptr;                                  \
}                                     \
  while (0)

 ...

#define STEP0_3_TABLE                                 \
/* Step 0: at the beginning.  */                          \
static JUMP_TABLE_TYPE step0_jumps[30] =                      \
{                                         \
  REF (form_unknown),                             \
  REF (flag_space),     /* for ' ' */                     \
  REF (flag_plus),      /* for '+' */                     \
  REF (flag_minus),     /* for '-' */                     \
  REF (flag_hash),      /* for '<hash>' */                \
  REF (flag_zero),      /* for '0' */                     \
  REF (flag_quote),     /* for '\'' */                    \
  REF (width_asterics), /* for '*' */                     \
  REF (width),      /* for '1'...'9' */               \
  REF (precision),      /* for '.' */                     \
  REF (mod_half),       /* for 'h' */                     \
  ...

我写了一个简单的示例,并且了解到&&do_##Name这行将do_##Name转换为指向void的指针。但是在这种情况下,我不了解指针算法的工作原理:#define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL有人可以写简单的说明吗?或编写一些指向Internet资源的链接,以便我可以阅读有关此技术的信息。

1 个答案:

答案 0 :(得分:2)

大概是为了确保具有衬里复杂性,该代码使用的跳转表由用作值的标签组成。

Labels-as-values是GNU C扩展,允许您使用&&来获取标签的地址。输入的地址为void *,然后您可以使用goto *address;跳转到该地址。

基本标签的一点点变化是,代码不是存储表中的绝对标签,而是存储do_uknown_form标签的偏移量。

这可以节省表中的空间(偏移量可以是4字节的int而不是8字节的指针),并有助于为共享库(因此#ifdef SHARED)甚至是static const生成更好的代码。当将代码加载到可重定位的共享库中时,绝对标签的跳转表需要进行修补,但是偏移量保持不变,因此对修补的需求消失了,并且该表可以存储在只读存储器中。

该技术在Ulrich Drepper的How to Write Shared Libraries文章中进行了描述。