【问题标题】:Pointer to void in C preprocessorC 预处理器中指向 void 的指针
【发布时间】:2019-06-15 11:22:36
【问题描述】:

我阅读了这个来源 (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' */                     \
  ...

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

【问题讨论】:

  • 处理器对C语言和指针一无所知
  • 您可能想看看预处理的 C 代码。使用 GCC,您可以通过 cpp file.c 看到它。
  • 这里使用 gcc goto 扩展 - 这不是很标准的 C
  • @P__J__ 和 void * 上的指针运算,另一个 gcc 扩展。
  • @melpomene 确实如此。但它是 gcc glibc :)。无论如何,这是一个非常有效的 gcc 代码,但不是我建议学习的代码。它被写成尽可能高效。当你获得更多经验时再回来

标签: c gcc goto pointer-arithmetic


【解决方案1】:

大概是为了保证具有线性复杂性,代码使用了一个由用作值的标签组成的跳转表。

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

与基本标签的小区别在于,代码不是在表中存储绝对标签,而是存储来自do_uknown_form 标签的偏移量。

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

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

【讨论】:

    猜你喜欢
    • 2021-12-24
    • 1970-01-01
    • 2010-09-29
    • 2015-05-24
    • 2018-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    相关资源
    最近更新 更多