写点什么

cstdio 的源码学习分析 11- 格式化输入输出函数 fprintf---format 解析跳转表逻辑分析

作者:桑榆
  • 2022-10-19
    广东
  • 本文字数:12216 字

    阅读完需:约 1 分钟

背景

在 stdio.h 的 printf 类型的函数中,要完成变量打印,主要有两个核心任务:一个是识别 format 占位符构造 printf_info 结构体,一个是打印该种特定类型的变量,这个跳转表机制就是为了识别 format 占位符而创建的。

%[flags][width][.precision][length]specifier对于这样的一个字符串类型识别,我们通常会采用自动机的方式进行识别,设定多个状态,每个状态代表一个识别位,状态之间的跳转需要条件,直到最后识别到specifier类型,这时,我们就能够完全解析出所有字段,上面构想的方法其实就是有限状态自动机的方法。

但是,因为 C 标准中规定了上面五个字段中可以有很多种组合,这个状态数量的叠加将是非常巨大的,我们能否寻找到一种比较简单的方式实现这个逻辑呢?

答案是有的,在 C 语言中有一种语法深为人所诟病,那就 goto 语句,可以进行无条件跳转到设定好的标号处,当然大部分情况下我们是不建议使用的,但是它在这种场景下就很契合,试想一下,我们的有限状态自动机中每一个状态是否就是一个标号呢?在这个标号中处理当前符号的识别,处理完成之后,移动到下一个字符,并通过我们设定好的跳转表跳转到下一个标号处进行处理,这个过程中,我们需要维护好的就是这个关键的跳转表,它记录了当前符号的下一个处理标号的位置,跳转表充当了有限状态自动机中触发状态变化函数的作用。

跳转表相关的宏定义/变量

1.jump_table---字符对应的跳转位置,字符在 L_(' ')和 L_('z')之间

其实这些字符的设定规律也比较明显:

  • 跳转值为 0 代表在 format 字符串解析过程中没有特殊含义的字符:如大写的 LMNOPQ 等;

  • 跳转值为 1 代表' ',2 代表'+',3 代表'-',4 代表'#',5 代表'0',6 代表'',7 代表'*',9 代表'.';

  • 跳转值 8 代表数字 1 到 9;

  • 跳转值 10 代表'h'(类型前缀,表示长度扩展信息,如 hd 表示 short int);

  • 跳转值 11 代表'l'(类型前缀,表示长度扩展信息,如 ld 表示 long int);

  • 跳转值 12 代表'L'和'q''(类型前缀,表示长度扩展信息,如 Lf 表示 long long double)

  • 跳转值 13 代表'Z'和'z',size_t 类型

  • 跳转值 14 代表'%'

  • 跳转值 15 代表'd'和'i',有符号整型数据

  • 跳转值 16 代表'u',无符号整型数据

  • 跳转值 17 代表'o',无符号八进制整型数据

  • 跳转值 18 代表'X'和'x',无符号 16 进制整型数据

  • 跳转值 19 代表'E', 'e', 'F', 'f', 'G', 'g',浮点数

  • 跳转值 20 代表'c',字符类型

  • 跳转值 21 代表's', 'S',字符串类型

  • 跳转值 22 代表'p',指针类型

  • 跳转值 23 代表'n',数字类型

  • 跳转值 24 代表'm',标准错误类型

  • 跳转值 25 代表'C',宽字符类型

  • 跳转值 26 代表'A', 'a',浮点数 16 进制类型

  • 跳转值 27 代表't',ptrdiff_t 类型

  • 跳转值 28 代表'j',intmax_t 类型

  • 跳转值 29 代表'I',标识是否使用额外的字符解释,如在标准字符中使用到了宽字符

  • 跳转值 30 代表'B', 'b',二进制整型数据

/* This table maps a character into a number representing a class.  In   each step there is a destination label for each class.  */static const uint8_t jump_table[] =                                                                                                                        {    /* ' ' */  1,            0,            0, /* '#' */  4,           0, /* '%' */ 14,            0, /* '''*/  6,           0,            0, /* '*' */  7, /* '+' */  2,           0, /* '-' */  3, /* '.' */  9,            0,       /* '0' */  5, /* '1' */  8, /* '2' */  8, /* '3' */  8,    /* '4' */  8, /* '5' */  8, /* '6' */  8, /* '7' */  8,    /* '8' */  8, /* '9' */  8,            0,            0,              0,            0,            0,            0,              0, /* 'A' */ 26, /* 'B' */ 30, /* 'C' */ 25,           0, /* 'E' */ 19, /* F */   19, /* 'G' */ 19,           0, /* 'I' */ 29,            0,            0,       /* 'L' */ 12,            0,            0,            0,              0,            0,            0, /* 'S' */ 21,           0,            0,            0,            0,       /* 'X' */ 18,            0, /* 'Z' */ 13,            0,              0,            0,            0,            0,              0, /* 'a' */ 26, /* 'b' */ 30, /* 'c' */ 20,    /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19,    /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28,            0,       /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,    /* 'p' */ 22, /* 'q' */ 12,            0, /* 's' */ 21,    /* 't' */ 27, /* 'u' */ 16,            0,            0,       /* 'x' */ 18,            0, /* 'z' */ 13  };
复制代码

2.NOT_IN_JUMP_RANGE(Ch)---判断字符是否在跳转范围内

因为我们规定可以识别跳转的字符在 L_(' ')和 L_('z')之间,其他字符均视为未知字符

#define NOT_IN_JUMP_RANGE(Ch) ((Ch) < L_(' ') || (Ch) > L_('z'))
复制代码

3.CHAR_CLASS(Ch)---获取对应的跳转值

因为 jump_table 是按照 L_(' ')和 L_('z')之间编写的,所以将对应字符值减去 L_(' ')可以得到索引值,进而取出对应的跳转值

#define CHAR_CLASS(Ch) (jump_table[(INT_T) (Ch) - L_(' ')])
复制代码

4.LABEL(Name)---label 宏

定义 label 的格式,增加 do_前缀

如:LABEL (flag_space):表示do_flag_space:label

#define LABEL(Name) do_##Name
复制代码

5.JUMP_TABLE_TYPE---跳转表中存储的数据类型

# define JUMP_TABLE_TYPE const int
复制代码

6.JUMP_TABLE_BASE_LABEL---基础 label

即 do_form_unknown,未知格式跳转

# define JUMP_TABLE_BASE_LABEL do_form_unknown
复制代码

7.REF(Name)---计算与基础 label 的地址差

C 语言中可以使用 &&符号获取标签名的地址

# define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL
复制代码

8.JUMP(ChExpr, table)---根据表与输入字符进行跳转

  1. 首先计算当前输入字符的偏移:如果是有效字符,则返回表的对应跳转位置,否则返回 REF (form_unknown),即 0;

  2. 取得要跳转的标号的地址 base+offset;

  3. 进行跳转 goto *ptr。

这里其实可以把标号当做函数指针,goto 语句类似函数调用。

# 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)
复制代码

核心跳转表定义

以下跳转表都定义在 STEP0_3_TABLE 宏中

1.step0_jumps[31]---开始进行 format 占位符识别或识别到 flags 时使用

仔细看,我们可以看到,这里的顺序与 jump_table 的标号是保持一致的,比如我们前面的例子中 %c,在此处进行跳转时,会直接跳转到 REF (form_character),针对这样的简单 format,一般在这里就跳转成功了,进行相应的打印。

    /* Step 0: at the beginning.  */                          \    static JUMP_TABLE_TYPE step0_jumps[31] =                      \    {                                         \      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' */                     \      REF (mod_long),       /* for 'l' */                     \      REF (mod_longlong),   /* for 'L', 'q' */                \      REF (mod_size_t),     /* for 'z', 'Z' */                \      REF (form_percent),   /* for '%' */                     \      REF (form_integer),   /* for 'd', 'i' */                \      REF (form_unsigned),  /* for 'u' */                     \      REF (form_octal),     /* for 'o' */                     \      REF (form_hexa),      /* for 'X', 'x' */                \      REF (form_float),     /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \      REF (form_character), /* for 'c' */                     \      REF (form_string),    /* for 's', 'S' */                \      REF (form_pointer),   /* for 'p' */                     \      REF (form_number),    /* for 'n' */                     \      REF (form_strerror),  /* for 'm' */                     \      REF (form_wcharacter),    /* for 'C' */                     \      REF (form_floathex),  /* for 'A', 'a' */                \                                                                                                REF (mod_ptrdiff_t),      /* for 't' */                     \      REF (mod_intmax_t),       /* for 'j' */                     \      REF (flag_i18n),      /* for 'I' */                     \      REF (form_binary),    /* for 'B', 'b' */                \    };
复制代码

那么在哪些情况下会使用到这张表呢?通过代码中的分析,可以看到如下几种情况:

因为针对合法字符格式,在这些字符后跟着的应该是 step0_jumps 这样对应的格式,此时识别的就是 flags 相关的字符

format 占位符的一般形式:%[flags][width][.precision][length]specifier

(1).识别了 %符号后的第一个字符

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

(2).识别空格符之后

      /* ' ' flag.  */    LABEL (flag_space):      space = 1;      JUMP (*++f, step0_jumps);
复制代码

(3).识别'+'之后

      /* '+' flag.  */    LABEL (flag_plus):      showsign = 1;      JUMP (*++f, step0_jumps);
复制代码

(4).识别'-'之后

      /* The '-' flag.  */    LABEL (flag_minus):      left = 1;      pad = L_(' ');      JUMP (*++f, step0_jumps);
复制代码

(5).识别'#'之后

      /* The '#' flag.  */    LABEL (flag_hash):      alt = 1;      JUMP (*++f, step0_jumps);
复制代码

(6).识别‘0’之后

      /* The '0' flag.  */    LABEL (flag_zero):      if (!left)    pad = L_('0');      JUMP (*++f, step0_jumps);
复制代码

(7).识别'''之后


/* The ''' flag. */ LABEL (flag_quote): group = 1; ... JUMP (*++f, step0_jumps);
复制代码

(8).识别'I'之后

    LABEL (flag_i18n):      use_outdigits = 1;      JUMP (*++f, step0_jumps);
复制代码

2.step1_jumps[31]---识别到 width 相关的字符时使用

注意这个表与 step0_jumps 的对比,我们可以发现,如下字符对应的位置,都被置为了 REF (form_unknown):


' ','+','-','#','0',''','I'---因为在 flags 识别中出现了,后续不可能再出现了,如果出现,则认为是异常模式;


'*','1'...'9'---是 width 的识别关键字符,识别出现后,将会把相关字符取出,后续如果继续出现,那说明是异常模式


/* Step 1: after processing width.  */                    \static JUMP_TABLE_TYPE step1_jumps[31] =                      \{                                         \  REF (form_unknown),                             \  REF (form_unknown),   /* for ' ' */                     \  REF (form_unknown),   /* for '+' */                     \                                                                                            REF (form_unknown),   /* for '-' */                     \  REF (form_unknown),   /* for '<hash>' */                \  REF (form_unknown),   /* for '0' */                     \  REF (form_unknown),   /* for ''' */                    \  REF (form_unknown),   /* for '*' */                     \  REF (form_unknown),   /* for '1'...'9' */               \  REF (precision),      /* for '.' */                     \  REF (mod_half),       /* for 'h' */                     \  REF (mod_long),       /* for 'l' */                     \  REF (mod_longlong),   /* for 'L', 'q' */                \  REF (mod_size_t),     /* for 'z', 'Z' */                \  REF (form_percent),   /* for '%' */                     \  REF (form_integer),   /* for 'd', 'i' */                \  REF (form_unsigned),  /* for 'u' */                     \  REF (form_octal),     /* for 'o' */                     \  REF (form_hexa),      /* for 'X', 'x' */                \  REF (form_float),     /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \  REF (form_character), /* for 'c' */                     \  REF (form_string),    /* for 's', 'S' */                \  REF (form_pointer),   /* for 'p' */                     \  REF (form_number),    /* for 'n' */                     \  REF (form_strerror),  /* for 'm' */                     \  REF (form_wcharacter),    /* for 'C' */                     \  REF (form_floathex),  /* for 'A', 'a' */                \  REF (mod_ptrdiff_t),      /* for 't' */                     \  REF (mod_intmax_t),       /* for 'j' */                     \  REF (form_unknown),       /* for 'I' */                     \  REF (form_binary),    /* for 'B', 'b' */                \};
复制代码


使用表的情况:

(1).识别到'*'之后

      /* Get width from argument.  */    LABEL (width_asterics):    ...    JUMP (*f, step1_jumps);
复制代码

(2).识别到'1'...'9'之后

      /* Given width in format string.  */    LABEL (width):    ...    JUMP (*f, step1_jumps);
复制代码

3.step2_jumps[31]---识别到 precision 相关字符时使用

对比 step1_jumps 可以发现,'.' 位置被置为 REF (form_unknown)

    /* Step 2: after processing precision.  */                    \    static JUMP_TABLE_TYPE step2_jumps[31] =                      \    {                                         \      REF (form_unknown),                             \      REF (form_unknown),   /* for ' ' */                     \      REF (form_unknown),   /* for '+' */                     \      REF (form_unknown),   /* for '-' */                     \      REF (form_unknown),   /* for '<hash>' */                \      REF (form_unknown),   /* for '0' */                     \      REF (form_unknown),   /* for ''' */                    \      REF (form_unknown),   /* for '*' */                     \      REF (form_unknown),   /* for '1'...'9' */               \      REF (form_unknown),   /* for '.' */                     \      REF (mod_half),       /* for 'h' */                     \      REF (mod_long),       /* for 'l' */                     \      REF (mod_longlong),   /* for 'L', 'q' */                \      REF (mod_size_t),     /* for 'z', 'Z' */                \      REF (form_percent),   /* for '%' */                     \      REF (form_integer),   /* for 'd', 'i' */                \      REF (form_unsigned),  /* for 'u' */                     \      REF (form_octal),     /* for 'o' */                     \      REF (form_hexa),      /* for 'X', 'x' */                \      REF (form_float),     /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \      REF (form_character), /* for 'c' */                     \      REF (form_string),    /* for 's', 'S' */                \      REF (form_pointer),   /* for 'p' */                     \      REF (form_number),    /* for 'n' */                     \      REF (form_strerror),  /* for 'm' */                     \      REF (form_wcharacter),    /* for 'C' */                     \      REF (form_floathex),  /* for 'A', 'a' */                \                                                                                                REF (mod_ptrdiff_t),      /* for 't' */                     \      REF (mod_intmax_t),       /* for 'j' */                     \      REF (form_unknown),       /* for 'I' */                     \      REF (form_binary),    /* for 'B', 'b' */                \    };
复制代码

(1)识别到'*'后

    LABEL (precision):    ...    JUMP (*f, step2_jumps);
复制代码

4.step3a_jumps[31]---识别到第一个'h'修饰符之后使用

对比 step2_jumps 之后发现:

  • 原来'h'的位置从 REF (mod_half)变为 REF (mod_halfhalf),为了识别'hh'的情况

  • 'l','L','q','z','Z','E','e','F','f','G','g','c','s','S','p','m','C',’a’,'A','t','j',都被置为 REF (form_unknown),因为这些都是非法组合

    /* Step 3a: after processing first 'h' modifier.  */              \    static JUMP_TABLE_TYPE step3a_jumps[31] =                     \    {                                         \      REF (form_unknown),                             \      REF (form_unknown),   /* for ' ' */                     \      REF (form_unknown),   /* for '+' */                     \      REF (form_unknown),   /* for '-' */                     \      REF (form_unknown),   /* for '<hash>' */                \      REF (form_unknown),   /* for '0' */                     \      REF (form_unknown),   /* for ''' */                    \      REF (form_unknown),   /* for '*' */                     \      REF (form_unknown),   /* for '1'...'9' */               \      REF (form_unknown),   /* for '.' */                     \      REF (mod_halfhalf),   /* for 'h' */                     \      REF (form_unknown),   /* for 'l' */                     \      REF (form_unknown),   /* for 'L', 'q' */                \      REF (form_unknown),   /* for 'z', 'Z' */                \      REF (form_percent),   /* for '%' */                     \      REF (form_integer),   /* for 'd', 'i' */                \      REF (form_unsigned),  /* for 'u' */                     \      REF (form_octal),     /* for 'o' */                     \      REF (form_hexa),      /* for 'X', 'x' */                \      REF (form_unknown),   /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \      REF (form_unknown),   /* for 'c' */                     \      REF (form_unknown),   /* for 's', 'S' */                \      REF (form_unknown),   /* for 'p' */                     \      REF (form_number),    /* for 'n' */                     \      REF (form_unknown),   /* for 'm' */                     \      REF (form_unknown),   /* for 'C' */                     \      REF (form_unknown),   /* for 'A', 'a' */                \                                                                                                REF (form_unknown),       /* for 't' */                     \      REF (form_unknown),       /* for 'j' */                     \      REF (form_unknown),       /* for 'I' */                     \      REF (form_binary),    /* for 'B', 'b' */                \    };
复制代码

(1).识别到'h'后

      /* Process 'h' modifier.  There might another 'h' following.  */    LABEL (mod_half):      is_short = 1;      JUMP (*++f, step3a_jumps);
复制代码

5.step3b_jumps[31]---识别到第一个'l'修饰符之后使用

对比 step2_jumps 之后发现:

  • 原来'l'的位置从 REF (mod_long)变为 REF (mod_longlong),为了识别'll'的情况

  • 'h','L','q','z','Z','t','j'被置为 REF (form_unknown),因为这些都是非法组合

    /* Step 3b: after processing first 'l' modifier.  */              \    static JUMP_TABLE_TYPE step3b_jumps[31] =                     \    {                                         \      REF (form_unknown),                             \      REF (form_unknown),   /* for ' ' */                     \      REF (form_unknown),   /* for '+' */                     \      REF (form_unknown),   /* for '-' */                     \      REF (form_unknown),   /* for '<hash>' */                \      REF (form_unknown),   /* for '0' */                     \      REF (form_unknown),   /* for ''' */                    \      REF (form_unknown),   /* for '*' */                     \      REF (form_unknown),   /* for '1'...'9' */               \      REF (form_unknown),   /* for '.' */                     \      REF (form_unknown),   /* for 'h' */                     \      REF (mod_longlong),   /* for 'l' */                     \      REF (form_unknown),   /* for 'L', 'q' */                \      REF (form_unknown),   /* for 'z', 'Z' */                \      REF (form_percent),   /* for '%' */                     \      REF (form_integer),   /* for 'd', 'i' */                \      REF (form_unsigned),  /* for 'u' */                     \      REF (form_octal),     /* for 'o' */                     \      REF (form_hexa),      /* for 'X', 'x' */                \      REF (form_float),     /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \      REF (form_character), /* for 'c' */                     \      REF (form_string),    /* for 's', 'S' */                \      REF (form_pointer),   /* for 'p' */                     \      REF (form_number),    /* for 'n' */                     \      REF (form_strerror),  /* for 'm' */                     \      REF (form_wcharacter),    /* for 'C' */                     \      REF (form_floathex),  /* for 'A', 'a' */                \                                                                                                REF (form_unknown),       /* for 't' */                     \      REF (form_unknown),       /* for 'j' */                     \      REF (form_unknown),       /* for 'I' */                     \      REF (form_binary),    /* for 'B', 'b' */                \    }
复制代码

(1),识别到'l'之后

     /* Process 'l' modifier.  There might another 'l' following.  */    LABEL (mod_long):      is_long = 1;      JUMP (*++f, step3b_jumps);
复制代码

以下跳转表都定义在 STEP4_TABLE 宏中

6.step4_jumps[31]---识别具体类型时使用

这里同 step3a_jumps 对比:

  • 'h'位置从 REF (mod_halfhalf)变为 REF (form_unknown),表明'hh'识别完成

  • 还原了之前不能和'h'组合的类型

同 step3b_jumps 对比:

  • 'l'位置从 REF (mod_longlong)变为 REF (form_unknown),表明'll'识别完成

  • 还原了之前不能和'l'组合的类型

总的来说,这里就是识别所有可能类型的

    /* Step 4: processing format specifier.  */                   \    static JUMP_TABLE_TYPE step4_jumps[31] =                      \    {                                         \      REF (form_unknown),                             \      REF (form_unknown),   /* for ' ' */                     \      REF (form_unknown),   /* for '+' */                     \      REF (form_unknown),   /* for '-' */                     \      REF (form_unknown),   /* for '<hash>' */                \      REF (form_unknown),   /* for '0' */                     \      REF (form_unknown),   /* for ''' */                    \      REF (form_unknown),   /* for '*' */                     \      REF (form_unknown),   /* for '1'...'9' */               \      REF (form_unknown),   /* for '.' */                     \      REF (form_unknown),   /* for 'h' */                     \      REF (form_unknown),   /* for 'l' */                     \      REF (form_unknown),   /* for 'L', 'q' */                \      REF (form_unknown),   /* for 'z', 'Z' */                \      REF (form_percent),   /* for '%' */                     \      REF (form_integer),   /* for 'd', 'i' */                \      REF (form_unsigned),  /* for 'u' */                     \      REF (form_octal),     /* for 'o' */                     \      REF (form_hexa),      /* for 'X', 'x' */                \      REF (form_float),     /* for 'E', 'e', 'F', 'f', 'G', 'g' */        \      REF (form_character), /* for 'c' */                     \      REF (form_string),    /* for 's', 'S' */                \      REF (form_pointer),   /* for 'p' */                     \      REF (form_number),    /* for 'n' */                     \      REF (form_strerror),  /* for 'm' */                     \      REF (form_wcharacter),    /* for 'C' */                     \      REF (form_floathex),  /* for 'A', 'a' */                \                                                                                                REF (form_unknown),       /* for 't' */                     \      REF (form_unknown),       /* for 'j' */                     \      REF (form_unknown),       /* for 'I' */                     \      REF (form_binary),    /* for 'B', 'b' */                \    }
复制代码

(1).识别到'hh'之后

      /* Process 'hh' modifier.  */    LABEL (mod_halfhalf):      is_short = 0;      is_char = 1;      JUMP (*++f, step4_jumps);
复制代码

(2).识别到'L'、‘q’、'll'之后

      /* Process 'L', 'q', or 'll' modifier.  No other modifier is     allowed to follow.  */    LABEL (mod_longlong):      is_long_double = 1;      is_long = 1;      JUMP (*++f, step4_jumps);
复制代码

(3).识别到'z'之后

    LABEL (mod_size_t):      is_long_double = sizeof (size_t) > sizeof (unsigned long int);      is_long = sizeof (size_t) > sizeof (unsigned int);      JUMP (*++f, step4_jumps);
复制代码

(4).识别到't'之后

    LABEL (mod_ptrdiff_t):      is_long_double = sizeof (ptrdiff_t) > sizeof (unsigned long int);      is_long = sizeof (ptrdiff_t) > sizeof (unsigned int);      JUMP (*++f, step4_jumps);
复制代码

(5).识别到'j'之后

    LABEL (mod_intmax_t):      is_long_double = sizeof (intmax_t) > sizeof (unsigned long int);      is_long = sizeof (intmax_t) > sizeof (unsigned int);      JUMP (*++f, step4_jumps);
复制代码

(6).识别 specs 中的每一个特定类型时使用

  /* Now walk through all format specifiers and process them.  */  for (; (size_t) nspecs_done < nspecs; ++nspecs_done)    {      STEP4_TABLE;      ...      CHAR_T spec = specs[nspecs_done].info.spec;      /* Process format specifiers.  */      while (1)    {    ...      JUMP (spec, step4_jumps);     } }    
复制代码

总结

跳转表的核心任务是解析

format 占位符的一般形式:%[flags][width][.precision][length]specifier

通过使用五张跳转表(3a 与 3b 处理两种情况,看做一张),对应识别的五个字段,后一个字段相比前一个字段,可跳转范围减少,对应限制只有合法的组合才能出现,一旦出现异常模式则直接跳转到 REF (form_unknown)。

通过使用跳转表机制,将识别过程进行了拆分,使得逻辑更加清晰,每个阶段每张表任务不同,也不会造成干扰,值得我们学习借鉴。

发布于: 刚刚阅读数: 3
用户头像

桑榆

关注

北海虽赊,扶摇可接;东隅已逝,桑榆非晚! 2020-02-29 加入

Android手机厂商-相机软件系统工程师 爬山/徒步/Coding

评论

发布
暂无评论
cstdio的源码学习分析11-格式化输入输出函数fprintf---format解析跳转表逻辑分析_c++_桑榆_InfoQ写作社区