本文主要是介绍linux内核分析之vsprintf.c,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
#include <stdarg.h>
#include <string.h>
判断给定字符是不是0-9的数字
#define is_digit(c) ((c) >= '0' && (c) <= '9')
将给定数字字符串转换成整型
static int skip_atoi(const char **s)
{
int i=0;
判断字符是不是数字,如果是数字就累加
while (is_digit(**s))
i = i*10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD 1
#define SIGN 2
#define PLUS 4
#define SPACE 8
#define LEFT 16
#define SPECIAL 32
#define SMALL 64
内嵌汇编,定义除法运算
输出寄存器是eax和edx,一个存放商一个存放余数
除数是base,被除数你放在eax中
#define do_div(n,base) ({ /
int __res; /
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); /
__res; })
将指定数字转换成不同格式的数字字符串
static char * number(char * str, int num, int base, int size, int precision
,int type)
{
char c,sign,tmp[36];
const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
int i;
如果是小写则把数字字母转换表改写
if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";
如果是左对齐,需要把ZEROPAD标志去掉
if (type&LEFT) type &= ~ZEROPAD;
检查基数范围是否正确。
if (base<2 || base>36)
return 0;
如果设置了标志位ZEROPAD,则表示用0去填充他,否则用空格
c = (type & ZEROPAD) ? '0' : ' ' ;
如果设置了符号位,并且数字是负数,需要加上符号字符
if (type&SIGN && num<0) {
sign='-';
num = -num;
} else
如果是标志位PLUS设置,需要在正数前面加上+号,否则
如果设置了SPACE,则在符号位用空格填充,要不就用0填充
sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);
如果符号位被设置成非0值,需要将size大小减去1
if (sign) size--;
如果设置了SPECIAL标志位,需要在八进制前面加O字符,需要在十六进制前面加0x字符串,
并相应的调整size的大小
if (type&SPECIAL)
if (base==16) size -= 2;
else if (base==8) size--;
i=0;
将数值部分转换成字符串存入tmp中。
if (num==0)
tmp[i++]='0';
else while (num!=0)
tmp[i++]=digits[do_div(num,base)];
如果超过了精度规定的长度,则重置精度值
if (i>precision) precision=i;
调整size的值
size -= precision;
如果既没设置ZEROPAD标志,也没设置LEFT标志,将剩下的部分用空格填充,也就是将字符串最开始的size个字符位设置
为空格
if (!(type&(ZEROPAD+LEFT)))
while(size-->0)
*str++ = ' ';
接着设置符号位
if (sign)
*str++ = sign;
如果是八进制或者十六进制,需要设置0或者0x字符
if (type&SPECIAL)
if (base==8)
*str++ = '0';
else if (base==16) {
*str++ = '0';
*str++ = digits[33];
}
如果没有设置左对齐,则需要进行填充
if (!(type&LEFT))
while(size-->0)
*str++ = c;
填充精度范围部分,用‘0’进行填充
while(i<precision--)
*str++ = '0';
设置数字部分
while(i-->0)
*str++ = tmp[i];
用空格填剩余部分
while(size-->0)
*str++ = ' ';
return str;
}
格式化输出:%[flags][width][.precision][|h|l|L][type]
int vsprintf(char *buf, const char *fmt, va_list args)
{
int len;
int i;
char * str;
char *s;
int *ip;
int flags;
int field_width;
int precision;
int qualifier;
对格式字符串进行扫描,把va_list中的数进行格式化,存入str中
for (str=buf ; *fmt ; ++fmt) {
如果不是%号,那么仅仅做简单的付值
if (*fmt != '%') {
*str++ = *fmt;
continue;
}
如果是%号
flags = 0;
repeat:
++fmt;
判断flags,并设置相应的位
switch (*fmt) {
case '-': flags |= LEFT; goto repeat;
case '+': flags |= PLUS; goto repeat;
case ' ': flags |= SPACE; goto repeat;
case '#': flags |= SPECIAL; goto repeat;
case '0': flags |= ZEROPAD; goto repeat;
}
field_width = -1;
紧接着判断下一个字符是不是数字,如果是则表示域宽
否则如果是*,表示下一个参数指定域宽,用va_arg来取得
下一个参数值
if (is_digit(*fmt))
field_width = skip_atoi(&fmt);
else if (*fmt == '*') {
field_width = va_arg(args, int);
if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
}
取得精度
precision = -1;
如果下一个字符是‘.’,表示有精度值,同样精度值如果是一个数字
就直接表示精度了,如果是字符*,表示从参数列表中取得精度
if (*fmt == '.') {
++fmt;
if (is_digit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
precision = va_arg(args, int);
}
if (precision < 0)
precision = 0;
}
取修饰符
qualifier = -1;
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
qualifier = *fmt;
++fmt;
}
最后取得格式
switch (*fmt) {
如果格式化成字符
case 'c':
如果不是左对齐在前面填充域宽个空格
if (!(flags & LEFT))
while (--field_width > 0)
*str++ = ' ';
接着填充参数列表中的值
*str++ = (unsigned char) va_arg(args, int);
如果是左对齐,域宽的填充应放在后面
while (--field_width > 0)
*str++ = ' ';
break;
如果格式化成字符串
case 's':
取参数列表中的字符串
s = va_arg(args, char *);
len = strlen(s);
对精度或字符串长度重新付值
if (precision < 0)
precision = len;
else if (len > precision)
len = precision;
如果不是左对齐
if (!(flags & LEFT))
在域宽前面填充空格
while (len < field_width--)
*str++ = ' ';
填充字符串
for (i = 0; i < len; ++i)
*str++ = *s++;
如果不是左对齐,需要在后面填充空格
while (len < field_width--)
*str++ = ' ';
break;
如果格式化成八进制数
case 'o':
str = number(str, va_arg(args, unsigned long), 8,
field_width, precision, flags);
break;
如果格式化成指针
case 'p':
如果没有设置域宽,则需要设置域宽为8,0填充标志
if (field_width == -1) {
field_width = 8;
flags |= ZEROPAD;
}
str = number(str,
(unsigned long) va_arg(args, void *), 16,
field_width, precision, flags);
break;
如果格式化成十六进制
case 'x':
flags |= SMALL;
case 'X':
str = number(str, va_arg(args, unsigned long), 16,
field_width, precision, flags);
break;
如果格式化成整型
case 'd':
带符号数
case 'i':
flags |= SIGN;
case 'u':
str = number(str, va_arg(args, unsigned long), 10,
field_width, precision, flags);
break;
将到目前为止转换的字符个数保存到ip指针指定的位置
case 'n':
ip = va_arg(args, int *);
*ip = (str - buf);
break;
default:
如果格式转换符不是%,表示有错,将一个%写入,如果格式字符串还没遍历完
直接将字符写入,否则退出循环
if (*fmt != '%')
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
break;
}
}
*str = '/0';
return str-buf;
}
这篇关于linux内核分析之vsprintf.c的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!