简体   繁体   中英

Pointer to void in C preprocessor

I read this source ( https://github.com/lattera/glibc/blob/master/stdio-common/vfprintf.c ) and find some interesting lines, which I do not fully understand:

#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' */                     \
  ...

I wrote simple example and understand that this line &&do_##Name cast do_##Name to pointer to void. But I don't understand how works pointer arithmetic in this case: #define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL Can someone write simple explanation? Or write some link to Internet resource where I can read about this technique.

Presumably to be guaranteed to have liner complexity, the code is using a jump table made of labels used as values.

Labels-as-values is a GNU C extension which allows you to use && to take the address of a label. The address is typed void * and you can then use goto *address; to jump to it.

The little twist with the base label is that instead of storing absolute labels in the table, to code is storing offsets from a do_uknown_form label.

That saves space (the offset can be an 4 byte int instead of an 8 byte pointer) in the table and helps generated better code for shared libs (hence the #ifdef SHARED ) as even a static const jump table of absolute labels would need to be patched when the code is loaded in a relocatable shared library, but the offsets remain the same so then the need for patching vanishes and the table can be stored in read-only memory.

The technique is described in the How to Write Shared Libraries essay by Ulrich Drepper.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM