本帖最后由 machunleilei 于 2011-06-26 00:45 编辑
首先给出函数的申明,好有一个整体的把握。
staticnoinline_for_stack
char*number(char*buf, char*end, unsigned long longnum,
structprintf_spec spec)。
先说一下,关于结构printf_spec的定义:
structprintf_spec {
u8 type; /* format_type enum */
u8 flags; /* flags to number() */
u8 base; /* number base, 8, 10 or 16 only */
u8 qualifier; /* number qualifier, one of 'hHlLtzZ' */
s16 field_width; /* width of output field */
s16 precision; /* # of digits/chars */
};
其中 u8是unsigned char。s16就是signed int。
从上面的英文解释中,我们就能理解,成员变量type主要就是关于规格化类型使用的,而flag主要是一些定义的宏,在后面会介绍。base就是保存我们需要将整数转换成的数制。qualifier在该函数中没有使用,我也没有具体研究。field_width就是我们需要得到转换之后的数值的宽度,用过printf这个函数的同学应该都知道我们在用printf约束输出整数的宽度的时候,如果整数的实际宽度大于我们规定的宽度,那么也会将整个整数输出而不会发生截断,当然如果整数的宽度小于我们需要的输出宽度就会填充字符,具体填充什么东西我们可以自己指定,这个变量的作用跟我们使用printf中规定输出宽度一样。变量precision作用就是体现在转换之后的字符窜宽度要大于我们规定的宽度的时候,取大者,不进行截断。
现在还需要介绍几个宏的定义:
#defineZEROPAD 1 /* pad with zero */
#define SIGN 2 /* unsigned/signed long */
#define PLUS 4 /* show plus */
#define SPACE 8 /* space if plus */
#define LEFT 16 /* left justified */
#defineSMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
相信大家都能看懂上面的英文解释,在这里只简单的提一下
ZEROPAD用0来填充,SIGN我们需要注意整数的符号,PLUS显示+号,SPACE可以用‘ ’代替+,LEFT左边对齐,SMALL小写,也就是0~f表示,SPECIAL需要前缀。
说了这么多,现在让我们具体来看一下这个函数的实现细节:
staticnoinline_for_stack
char*number(char*buf, char*end, unsigned long longnum,
structprintf_spec spec)
{
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ";这个数组主要的作用
是方便我们在进行数值转换的时候,将整数变成字符类型 */ char tmp[66];/*这个主要用来临时保存将整数转换成的指定的进制的字符串*/ char sign;
charlocase;
intneed_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
/*判断首先我们规定了没有需要注意前缀,如果我们不需要前缀,那么当然就不需要了,同时也要满足不是10进制的*/ inti;
/* locase = 0 or 0x20. ORing digits or letters with 'locase'
* produces same digits or (maybe lowercased) letters */ locase = (spec.flags & SMALL)
/*判断是不是需要转换成小写的表示。*/ if(spec.flags & LEFT)/*判断是不是需要左对齐*/ spec.flags &= ~ZEROPAD;
sign = 0;
if (spec.flags & SIGN) {/*我们需不需要注意数值的正负,当然如果需要,就要判断是正数还是负数,具体怎样表示下面说的很清楚*/ if ((signed long long)num < 0) {
sign = '-';
num = -(signed long long)num;
spec.field_width--;
} else if (spec.flags & PLUS) {
sign = '+';
spec.field_width--;
} else if (spec.flags & SPACE) {//我们是否规定了用空格来代替+号了 sign = ' ';
spec.field_width--;
}
}
/*每一个分支中,我们都可以看出来spec.filed_width自减了,这是因为符号也占据了我们规定的长度。*/ if(need_pfx) { /*判断需不需要添加前缀,就是0x或者0.*/ spec.field_width--; /*不论是16进制或者是8进制,都会将宽度--,因为0x或者0的最下宽度是1.0x还需要-- */ if(spec.base == 16)
spec.field_width--;
}
/* generate full string in tmp[], in reverse order */
i = 0;
if(num == 0)
tmp[i++] = '0';
/* Generic code, for any base:
elsedo{
tmp[i++] = (digits[do_div(num,base)] | locase);
} while(num != 0);
*/
else if (spec.base != 10) { /* 8 or 16 */
intmask = spec.base - 1;
intshift = 3;
if(spec.base == 16)
shift = 4;
do{
tmp[i++] = (digits[((unsigned char)num) & mask] | locase);
num >>= shift;
} while(num);
} else{ /* base 10 */
i = put_dec(tmp, num) - tmp;
}/*上面的这段代码就是将数值转换成了字符串了,具体很容易理解。*/
/* printing 100 using %2d gives "100", not "00" */ if(i > spec.precision)
spec.precision = i;
/* leading space padding */
spec.field_width -= spec.precision;
if(!(spec.flags & (ZEROPAD+LEFT))) {
while(--spec.field_width >= 0) {
if(buf < end)
*buf = ' ';
++buf;
}
}/*判断数值转换成为了字符串之后长度如果超过了我们规定的长度,则不进行截断,同时要将precision修改成为实际的长度*/ /* sign */
if(sign) {
if(buf < end)
*buf = sign;
++buf;
} /*加入符号字符*/ /* "0x" / "0" prefix */
if(need_pfx) {
if(buf < end)
*buf = '0';
++buf;
if(spec.base == 16) {
if(buf < end)
*buf = ('X' | locase);
++buf;
}
}
/* zero or space padding */
if(!(spec.flags & LEFT)) {
charc = (spec.flags & ZEROPAD) ? '0' : ' ';
while(--spec.field_width >= 0) {
if(buf < end)
*buf = c;
++buf;
}
}
/* hmm even more zero padding? */
while(i <= --spec.precision) {
if(buf < end)
*buf = '0';
++buf;
} /*填充0*/
/* actual digits of result */ while (--i >= 0) {
if(buf < end)
*buf = tmp;
++buf;
}
/* trailing space padding */
while(--spec.field_width >= 0) {
if(buf < end)
*buf = ' ';
++buf;
}
return buf;
}