Указатель на void в препроцессоре C - PullRequest
1 голос
/ 15 июня 2019

Я читаю этот источник (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 cast do_##Name указатель на void. Но я не понимаю, как работает арифметика указателя в этом случае: #define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL Может кто-нибудь написать простое объяснение? Или написать какую-нибудь ссылку на интернет-ресурс, где я могу прочитать об этой технике.

1 Ответ

2 голосов
/ 15 июня 2019

Предположительно, чтобы гарантировать сложность лайнера, в коде используется таблица переходов, состоящая из меток, используемых в качестве значений.

Labels-as-values ​​ - это расширение GNU C, которое позволяетВы должны использовать &&, чтобы взять адрес этикетки.Адрес набирается void *, и затем вы можете использовать goto *address; для перехода к нему.

Небольшой поворот с базовой меткой заключается в том, что вместо сохранения абсолютных меток в таблице, код сохраняет смещения изdo_uknown_form label.

Это экономит место (смещение может быть 4-байтовым int вместо 8-байтового указателя) в таблице и помогает генерировать лучший код для общих библиотек (отсюда #ifdef SHARED) какдаже таблица абсолютных меток static const должна быть исправлена, когда код загружен в перемещаемую разделяемую библиотеку, но смещения остаются такими же, поэтому необходимость исправления исчезает, и таблица может быть сохранена в постоянной памяти,

Методика описана в эссе Ульриха Дреппера Как писать общие библиотеки .

...