Glibc---_fitoa_word的实现:一个整型数据是如何转成字符串的呢?
原创
©著作权归作者所有:来自51CTO博客作者桑榆晚见天的原创作品,请联系作者获取转载授权,否则将追究法律责任
引言
_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被定义为
- char *buf:本次的输出字符串开始指针
- unsigned int base:进制
- int upper_case:标识是否需要大写(如0xf与0XF)
- char *:标识最后一个写入buf的字符的指针的下一个位置
函数实现
代码位置:glibc/stdio-common/_itoa.c
_fitoa_word逻辑
可以看到,这里的实现实际上还是转交给到了_itoa_word函数,但是在_fitoa_word中也做了如下的处理:
- 申请临时buffer tmpbuf,用于_fitoa_word的生成字符串;
注意:这里选择的buffer大小sizeof (value) * 4,考虑到我们进行进制转换时,相同的整型数据,越高的进制生成的字符串越短,所以最坏的情况就是二进制全部用01进行填充,那就应该是有多少位,每个字节8位,所以是sizeof (value) * 8,但这里是*4。
- 调用_itoa_word;
- 依次将转换后的字符串拷贝到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中的函数,将一个整型数据转换为对应进制的字符串数据,主要是调用了除数取余法实现。