C语言之linux内核可变参实现printf,sprintf

      昨天,我发表了一篇用可变参实现的fprintf函数,其实说实话还不完全是可变参实现的,因为用到了FILE * 这样的指针,需要包含stdio.h这个头文件才能实现这个函数,今天我们就来看看,如何抛弃stdio.h,全0开始实现printf , sprintf ,当然,这段代码是我在linux内核里面获取的,再经过我本人修改,移植,在DevC++这个编译环境中通过测试。我们来看看代码:生气生气

     

[cpp]  view plain  copy
 print ?
  1. #include <stdarg.h>  
  2. #define NULL 0  
  3. //如果字符串中为数字,则返回数字   
  4. static int skip_atoi(const char **s)  
  5. {  
  6.     int i = 0;  
  7.   
  8.     while (isdigit(**s))  
  9.         i = i * 10 + *((*s)++) - '0';  
  10.     return i;  
  11. }  
  12.   
  13. static inline int isdigit(int ch)  
  14. {  
  15.     return (ch >= '0') && (ch <= '9'); //返回从字符中提取0-9的数字  
  16. }     
  17.   
  18. #define ZEROPAD 1       /* pad with zero */  
  19. #define SIGN    2       /* unsigned/signed long */  
  20. #define PLUS    4       /* show plus */  
  21. #define SPACE   8       /* space if plus */  
  22. #define LEFT    16      /* left justified */  
  23. #define SMALL   32      /* Must be 32 == 0x20 */  
  24. #define SPECIAL 64      /* 0x */  
  25.   
  26. //这个宏主要用来实现判断是要转化成什么进制数   
  27. #define __do_div(n, base) ({ \  
  28. int __res; \  
  29. __res = ((unsigned long) n) % (unsigned) base; \  
  30. n = ((unsigned long) n) / (unsigned) base; \  
  31. __res; })  
  32.   
  33. static char *number(char *str, long num, int base, int size, int precision,  
  34.             int type)  
  35. {  
  36.     /*这个字符串数组存放着0-15这16个数字,到时候要用来进制转换*/  
  37.     static const char digits[16] = "0123456789ABCDEF";   
  38.   
  39.     char tmp[66];  
  40.     char c, sign, locase;  
  41.     int i;  
  42.   
  43.     /*locase = 0 或者 0x20 , 产生与locase相同的数字或字母,也许字母是小写的*/  
  44.     locase = (type & SMALL);  
  45.     if (type & LEFT)  
  46.         type &= ~ZEROPAD;  
  47.     if (base < 2 || base > 36)  
  48.         return NULL;  
  49.     c = (type & ZEROPAD) ? '0' : ' ';  
  50.     sign = 0;  
  51.     if (type & SIGN) {  
  52.         if (num < 0) {  
  53.             sign = '-';  
  54.             num = -num;  
  55.             size--;  
  56.         } else if (type & PLUS) {  
  57.             sign = '+';  
  58.             size--;  
  59.         } else if (type & SPACE) {  
  60.             sign = ' ';  
  61.             size--;  
  62.         }  
  63.     }  
  64.     //检测进制数,是要2进制还是要8进制还是16进制   
  65.     if (type & SPECIAL) {  
  66.         if (base == 16)  
  67.             size -= 2;  
  68.         else if (base == 8)  
  69.             size--;  
  70.     }  
  71.     i = 0;  
  72.     if (num == 0)  
  73.         tmp[i++] = '0';  
  74.     else  
  75.         while (num != 0)  
  76.             tmp[i++] = (digits[__do_div(num, base)] | locase);  
  77.     if (i > precision)  
  78.         precision = i;  
  79.     size -= precision;  
  80.     if (!(type & (ZEROPAD + LEFT)))  
  81.         while (size-- > 0)  
  82.             *str++ = ' ';  
  83.     if (sign)  
  84.         *str++ = sign;  
  85.     if (type & SPECIAL) {  
  86.         if (base == 8)  
  87.             *str++ = '0';  
  88.         else if (base == 16) {  
  89.             *str++ = '0';  
  90.             *str++ = ('X' | locase);  
  91.         }  
  92.     }  
  93.     if (!(type & LEFT))  
  94.         while (size-- > 0)  
  95.             *str++ = c;  
  96.     while (i < precision--)  
  97.         *str++ = '0';  
  98.     while (i-- > 0)  
  99.         *str++ = tmp[i];  
  100.     while (size-- > 0)  
  101.         *str++ = ' ';  
  102.     return str;  
  103. }  
  104.   
  105. int vsprintf(char *buf, const char *fmt, va_list args)  
  106. {  
  107.     int len;  
  108.     unsigned long num;  
  109.     int i, base;  
  110.     char *str;  
  111.     const char *s;  
  112.     int flags;        
  113.     int field_width;    /*位宽输出*/  
  114.     int precision;        
  115.     int qualifier;        
  116.     //这里判断,如果在字符串fmt中不存在%这个符号,那么字符串继续往后遍历   
  117.     for (str = buf; *fmt; ++fmt) {  
  118.         if (*fmt != '%') {  
  119.             *str++ = *fmt;  
  120.             continue;  
  121.         }  
  122.   
  123.         //程序设置标志位   
  124.         flags = 0;  
  125.           repeat:  
  126.         ++fmt;      /* this also skips first '%' */  
  127.         //格式控制   
  128.         switch (*fmt) {  
  129.         case '-':  
  130.             flags |= LEFT;  
  131.             goto repeat;  
  132.         case '+':  
  133.             flags |= PLUS;  
  134.             goto repeat;  
  135.         case ' ':  
  136.             flags |= SPACE;  
  137.             goto repeat;  
  138.         case '#':  
  139.             flags |= SPECIAL;  
  140.             goto repeat;  
  141.         case '0':  
  142.             flags |= ZEROPAD;  
  143.             goto repeat;  
  144.         }  
  145.   
  146.         //获取宽度,这里主要是要实现printf的位宽机制   
  147.         field_width = -1;  
  148.         if (isdigit(*fmt))  
  149.             field_width = skip_atoi(&fmt);  
  150.         else if (*fmt == '*') {  
  151.             ++fmt;  
  152.             field_width = va_arg(args, int);  
  153.             if (field_width < 0) {  
  154.                 field_width = -field_width;  
  155.                 flags |= LEFT;  
  156.             }  
  157.         }  
  158.   
  159.         precision = -1;  
  160.         if (*fmt == '.') {  
  161.             ++fmt;  
  162.             if (isdigit(*fmt))  
  163.                 precision = skip_atoi(&fmt);  
  164.             else if (*fmt == '*') {  
  165.                 ++fmt;  
  166.                 precision = va_arg(args, int);  
  167.             }  
  168.             if (precision < 0)  
  169.                 precision = 0;  
  170.         }  
  171.   
  172.         /*得到的转换限定符*/  
  173.         qualifier = -1;  
  174.         if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {  
  175.             qualifier = *fmt;  
  176.             ++fmt;  
  177.         }  
  178.   
  179.         /*默认进制为10进制*/  
  180.         base = 10;  
  181.         //以下主要是要实现printf的格式输出 例如:%d , %c , %u ...等等   
  182.         switch (*fmt) {  
  183.         case 'c':  //以字符形式进行输出   
  184.             if (!(flags & LEFT))  
  185.                 while (--field_width > 0)  
  186.                     *str++ = ' ';  
  187.             *str++ = (unsigned char)va_arg(args, int);  
  188.             while (--field_width > 0)  
  189.                 *str++ = ' ';  
  190.             continue;  
  191.   
  192.         case 's'//以字符串形式进行输出   
  193.             s = va_arg(args, char *);  
  194.             len = strnlen(s, precision);  
  195.   
  196.             if (!(flags & LEFT))  
  197.                 while (len < field_width--)  
  198.                     *str++ = ' ';  
  199.             for (i = 0; i < len; ++i)  
  200.                 *str++ = *s++;  
  201.             while (len < field_width--)  
  202.                 *str++ = ' ';  
  203.             continue;  
  204.   
  205.         case 'p'//以地址形式输出,也就是以16进制数输出   
  206.             if (field_width == -1) {  
  207.                 field_width = 2 * sizeof(void *);  
  208.                 flags |= ZEROPAD;  
  209.             }  
  210.             str = number(str,  
  211.                      (unsigned long)va_arg(args, void *), 16,  
  212.                      field_width, precision, flags);  
  213.             continue;  
  214.   
  215.         case 'n':  
  216.             if (qualifier == 'l') {  
  217.                 long *ip = va_arg(args, long *);  
  218.                 *ip = (str - buf);  
  219.             } else {  
  220.                 int *ip = va_arg(args, int *);  
  221.                 *ip = (str - buf);  
  222.             }  
  223.             continue;  
  224.   
  225.         case '%':     //这里表示字符串中存在%号这个字符   
  226.             *str++ = '%';  
  227.             continue;  
  228.   
  229.             /* integer number formats - set up the flags and "break" */  
  230.         case 'o':     //%o 表示8进制输出   
  231.             base = 8;  
  232.             break;  
  233.   
  234.         case 'x':     //%x或者%X 表示16进制输出   
  235.             flags |= SMALL;  
  236.         case 'X':  
  237.             base = 16;  
  238.             break;  
  239.   
  240.         case 'd':     //%d %i整形数输出,%u无符号整形   
  241.         case 'i':  
  242.             flags |= SIGN;  
  243.         case 'u':  
  244.             break;  
  245.   
  246.         default:  
  247.             *str++ = '%';  
  248.             if (*fmt)  
  249.                 *str++ = *fmt;  
  250.             else  
  251.                 --fmt;  
  252.             continue;  
  253.         }  
  254.         if (qualifier == 'l'//以无符号长整型输出   
  255.             num = va_arg(args, unsigned long);  
  256.         else if (qualifier == 'h') {  
  257.             num = (unsigned short)va_arg(args, int);  
  258.             if (flags & SIGN)  
  259.                 num = (short)num;  
  260.         } else if (flags & SIGN)  
  261.             num = va_arg(args, int);  
  262.         else  
  263.             num = va_arg(args, unsigned int);  
  264.         str = number(str, num, base, field_width, precision, flags);  
  265.     }  
  266.     *str = '\0';   //字符串遍历到有\0的地方就停止   
  267.     return str - buf;  
  268. }  
  269. //可变参形式实现sprintf   
  270. int mysprintf(char *buf, const char *fmt, ...)  
  271. {  
  272.     va_list args;  
  273.     int i;  
  274.     va_start(args, fmt);  
  275.     //将获取到的fmt格式字符串写入到buf这个缓存里去   
  276.     i = vsprintf(buf, fmt, args);  
  277.     //释放args   
  278.     va_end(args);  
  279.     return i;  
  280. }  
  281.   
  282. //可变参形式进行实现myprintf   
  283. int myprintf(const char *fmt, ...)  
  284. {  
  285.     char printf_buf[1024];  
  286.     va_list args;  
  287.     int printed;  
  288.     va_start(args, fmt);  
  289.     printed = vsprintf(printf_buf, fmt, args);  
  290.     va_end(args);  
  291.     puts(printf_buf);  
  292.     return printed;  
  293. }  
  294.   
  295. int main(void)  
  296. {  
  297.     myprintf("输出字符串:hello world!\n")    ;  
  298.     static int sum , a = 3 , b = 4;   
  299.     sum = a + b ;   
  300.     myprintf("sum(十进制输出):%d\n",sum);  
  301.     myprintf("sum(16进制输出):%p\n",sum);  
  302.     char buffer[128] = {0};  
  303.     //将字符串存到一个数组buffer里去   
  304.     mysprintf(buffer , "输出字符串:hello world!\n");  
  305.     //以字符串格式输出这个buffer的内容   
  306.     myprintf("buffer:%s\n",buffer);  
  307.     return 0 ;  
  308. }  
运行结果:

看完代码就知道了,我们这个程序没有包含stdio.h这个头文件,一样也就实现了printf和sprintf这两个函数。这样的话,以后如果要自己实现一个printf函数,这份代码就可以作为一个文件来进行调用了,哈哈!大笑



 
转自:http://blog.csdn.net/morixinguan/article/details/50725779
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值