写点什么

_fitoa_word 的实现:一个整型数据是如何转成字符串的呢?

作者:桑榆
  • 2022 年 10 月 09 日
    广东
  • 本文字数:2261 字

    阅读完需:约 7 分钟

引言

_fitoa_word 是一个将 int 型数据转换为对应进制的 char 类型的函数,在通常的整型数据处理中非常常见,用来打印一个整型数据对应的 N 进制表示,对于其原理,实际上就是我们中学时候学习的除数取余法,然后再选取对应的符号进行表示。那么在 Glibc 源码中,它又是如何实现的呢?接下来,我们一起来看看对应的实现。

函数定义

参考代码:glibc/sysdeps/generic/_itoa.h

/* Similar to the _itoa functions, but output starts at buf and pointer                                                                                                                                        after the last written character is returned.  */extern char *_fitoa_word (_ITOA_WORD_TYPE value, char *buf,              unsigned int base,              int upper_case) attribute_hidden;
复制代码

其中:

  • 入参

    _ITOA_WORD_TYPE value:是本次需要转换的整型量,_ITOA_WORD_TYPE 被定义为

  • #define _ITOA_WORD_TYPE unsigned long int,即 64 位系统下 8 个字节大小的整型变量

  • char *buf:本次的输出字符串开始指针

  • unsigned int base:进制

  • int upper_case:标识是否需要大写(如 0xf 与 0XF)

  • 出参

    char *:标识最后一个写入 buf 的字符的指针的下一个位置

函数实现

代码位置:glibc/stdio-common/_itoa.c

_fitoa_word 逻辑

可以看到,这里的实现实际上还是转交给到了_itoa_word 函数,但是在_fitoa_word 中也做了如下的处理:

  1. 申请临时 buffer tmpbuf,用于_fitoa_word 的生成字符串;

注意:这里选择的 buffer 大小 sizeof (value) * 4,考虑到我们进行进制转换时,相同的整型数据,越高的进制生成的字符串越短,所以最坏的情况就是二进制全部用 01 进行填充,那就应该是有多少位,每个字节 8 位,所以是 sizeof (value) * 8,但这里是*4。

  1. 调用_itoa_word;

  2. 依次将转换后的字符串拷贝到 buf 中,后置递增运算符是先使用当前值,然后再递增。

char *_fitoa_word (_ITOA_WORD_TYPE value, char *buf, unsigned int base,                                                                                                                                                    int upper_case){  char tmpbuf[sizeof (value) * 4];        /* Worst case length: base 2.  */  char *cp = _itoa_word (value, tmpbuf + sizeof (value) * 4, base, upper_case);  while (cp < tmpbuf + sizeof (value) * 4)    *buf++ = *cp++;  return buf;}
复制代码

_itoa_word 函数参数

与_fitoa_word 基本一致,但要注意,这里的 char *buflim 指向的是分配的临时 buffer 的最后一个可写位置的下一个地址:tmpbuf + sizeof (value) * 4

extern char *_itoa_word (_ITOA_WORD_TYPE value, char *buflim,                                                                                                                                                            unsigned int base,             int upper_case) attribute_hidden;
复制代码

_itoa_word 函数逻辑

1.根据 upper_case 选择对应的字符集

根据是否需要大写选择对应的字符集,_itoa_upper_digits 或 _itoa_lower_digits。

char *_itoa_word (_ITOA_WORD_TYPE value, char *buflim,        unsigned int base, int upper_case){   const char *digits = (upper_case            ? _itoa_upper_digits            : _itoa_lower_digits);
// glibc/stdio-common/itoa-udigits.c/* Upper-case digits. */const char _itoa_upper_digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// glibc/stdio-common/itoa-digits.c/* Lower-case digits. */const char _itoa_lower_digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz";
复制代码

2.使用除数取余法进行进制转换

注意,这里定义了一个宏 SPECIAL(Base),针对我们常用的 10 进制、16 进制、8 进制做了简化,但实际逻辑还是与 default 的一致。

  • 先对 value 使用 %取余数,得到对应的符号;

  • 注意--buflim 是先做递减运算,再使用该值,第一次递减则刚好是上面 tmpbuf 的最后一个位置;

  • 使用*取地址并进行赋值;

  • 对 value 做除法,直到除数为 0,否则重复上面的步骤。

上面就是一个标准的除数取余法。

  switch (base)    {#define SPECIAL(Base)                                 \    case Base:                                    \      do                                      \    *--buflim = digits[value % Base];                     \      while ((value /= Base) != 0);                       \      break
SPECIAL (10); SPECIAL (16); SPECIAL (8); default: do *--buflim = digits[value % base]; while ((value /= base) != 0); }
复制代码

3.返回 buflim 指针

注意,这里返回的 buflim 实际上最后指向的是第一个字符;

然后 undef SPECIAL,避免后续使用出现覆盖异常

  return buflim;}#undef SPECIAL
复制代码

总结

_fitoa_word 函数是 Glibc 中的函数,将一个整型数据转换为对应进制的字符串数据,主要是调用了除数取余法实现。

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

桑榆

关注

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

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

评论

发布
暂无评论
_fitoa_word的实现:一个整型数据是如何转成字符串的呢?_源码刨析_桑榆_InfoQ写作社区