c - 为什么宏在 libc 中可以滥用

标签 c libc

<分区>

如果我告诉你我想实现下面的代码:

static const uint8_t jump_table[] =
{
    /* ' ' */  1,            0,            0, /* '#' */  4,
               0, /* '%' */ 14,            0, /* '\''*/  6,
      ...
      ...
    /* 't' */ 27, /* 'u' */ 16,            0,            0,
    /* 'x' */ 18,            0, /* 'z' */ 13
};

#define LABEL(Name) do_##Name
#define NOT_IN_JUMP_RANGE(Ch) ((Ch) < ' ' || (Ch) > 'z')
#define CHAR_CLASS(Ch) (jump_table[(INT_T) (Ch) - ' '])
#define REF(Name) &&do_##Name
#define JUMP(ChExpr, table)                   \
do                                            \
{                                             \
    const void *ptr;                          \
    spec = (ChExpr);                          \
    ptr = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown) \
       : table[CHAR_CLASS (spec)];            \
    goto *ptr;                                \
} while (0)

#define 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_i18n),        /* for 'I' */     \
};

使用这个例子:

void usage_example_function(void)
{
    do
    {
      TABLE;

      /* Get current character in format string.  */
      JUMP (*++f, step0_jumps);

      /* ' ' flag.  */
      LABEL (flag_space):
      space = 1;
      JUMP (*++f, step0_jumps);
      ...
      ...
    } while (something)
}

你会告诉我,在任何体面的编码风格标准下,这是 Not Acceptable ,并且提交这样的代码很可能弊大于利。

现在,glibc 实现了 vfprintf有比这更多的宏滥用(上面的代码取自那里),但这段代码是这么多编译程序的一部分,并且经过了如此多次测试,这让我觉得今天的任何(宏)编码标准都缺乏理由。

为什么这样的宏滥用是错误的?或者,如果这种滥用是错误的,我们为什么要接受这样的 libc 实现?

最佳答案

  1. 风格规则是旨在改善软件工程师(包括团队中的当前工程师、 future 将从事代码工作的工程师以及 future 的自己)之间的沟通以及减少错误和进行软件开发的指南更高效。这些指南应与其他目标相平衡,例如性能、必要性、解决特定情况下出现的问题等。

  2. 与编译器和语言实现相关联的库中的软件通常需要用于特殊目的。它可能是 C 实现的一部分,并且可能需要与 C 编译器协调。因此,它并不完全受制于严格符合 C 代码的约束——它可以使用特定于 C 实现的结构或特性。 (这种使用应该清楚地记录下来。)通常,不可能用严格符合 C 代码的方式编写 C 实现——C 实现必须以 C 标准未指定的方式与硬件和操作系统软件交互。出于性能和其他目标的原因,即使可能,也可能并不理想。

  3. 图书馆服务的一个目的是完成所有“肮脏”的工作,这样您就不必去做,或者完成艰苦或复杂的工作。旨在供许多人使用的图书馆是投入大量工程努力的好地方,因为少数人的工作会为许多人带来返回。考虑到 glibc 的直接和间接用户数量,投资返回的杠杆作用是巨大的,因此如果能提高性能或可移植性,值得编写一些奇怪的代码。在某种程度上,需要这种复杂的代码才能获得所需的结果,或者需要替代方法,将这些代码放入库中可以减少世界上此类代码的数量,方法是将它放在一个地方供许多人使用这样他们就不需要编写自己的类似代码来实现相同的目的。

  4. 与某些代码相比,问题中显示的代码对预处理器宏的使用相当温和。除了 GCC“标签作为值”扩展(使用 && 来获取标签的地址)之外,它使用标准功能并构建一个状态机,任何拥有软件学士学位的人都应该熟悉工程。在对代码进行一些研究之后,有经验的软件工程师可以识别它在做什么并相当轻松地使用代码。

关于c - 为什么宏在 libc 中可以滥用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53829546/

相关文章:

c - 有没有不需要linux内核的libc项目

python - 使用 Pythons ctypes 从 libc 调用 uname

java - 发射时的致命信号 (KitKat)

c++ - 将未对齐的 double 加载到 _m128d 寄存器

c - 如何获取操作系统提供的 dlopen()/dlsym() 地址

C:sizeof()、sizeof()和sizeof

c - 根据 C 标准,带参数的宏可能会与标识符发生冲突吗?

linux - Libc的静态链接

c - 如何选择要打开的文件?

c - 使用信号量的进程之间的互斥