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;
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值