版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan。若是侵权用于商业用途,请联系博主,否则将追究责任 http://blog.csdn.net/morixinguan/article/details/50725779
昨天,我发表了一篇用可变参实现的fprintf函数,其实说实话还不完全是可变参实现的,因为用到了FILE * 这样的指针,需要包含stdio.h这个头文件才能实现这个函数,今天我们就来看看,如何抛弃stdio.h,全0开始实现printf , sprintf ,当然,这段代码是我在linux内核里面获取的,再经过我本人修改,移植,在DevC++这个编译环境中通过测试。我们来看看代码:
- #include <stdarg.h>
- #define NULL 0
- //如果字符串中为数字,则返回数字
- static int skip_atoi(const char **s)
- {
- int i = 0;
- while (isdigit(**s))
- i = i * 10 + *((*s)++) - '0';
- return i;
- }
- static inline int isdigit(int ch)
- {
- return (ch >= '0') && (ch <= '9'); //返回从字符中提取0-9的数字
- }
- #define ZEROPAD 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 */
- #define SMALL 32 /* Must be 32 == 0x20 */
- #define SPECIAL 64 /* 0x */
- //这个宏主要用来实现判断是要转化成什么进制数
- #define __do_div(n, base) ({ \
- int __res; \
- __res = ((unsigned long) n) % (unsigned) base; \
- n = ((unsigned long) n) / (unsigned) base; \
- __res; })
- static char *number(char *str, long num, int base, int size, int precision,
- int type)
- {
- /*这个字符串数组存放着0-15这16个数字,到时候要用来进制转换*/
- static const char digits[16] = "0123456789ABCDEF";
- char tmp[66];
- char c, sign, locase;
- int i;
- /*locase = 0 或者 0x20 , 产生与locase相同的数字或字母,也许字母是小写的*/
- locase = (type & SMALL);
- if (type & LEFT)
- type &= ~ZEROPAD;
- if (base < 2 || base > 36)
- return NULL;
- c = (type & ZEROPAD) ? '0' : ' ';
- sign = 0;
- if (type & SIGN) {
- if (num < 0) {
- sign = '-';
- num = -num;
- size--;
- } else if (type & PLUS) {
- sign = '+';
- size--;
- } else if (type & SPACE) {
- sign = ' ';
- size--;
- }
- }
- //检测进制数,是要2进制还是要8进制还是16进制
- if (type & SPECIAL) {
- if (base == 16)
- size -= 2;
- else if (base == 8)
- size--;
- }
- i = 0;
- if (num == 0)
- tmp[i++] = '0';
- else
- while (num != 0)
- tmp[i++] = (digits[__do_div(num, base)] | locase);
- if (i > precision)
- precision = i;
- size -= precision;
- if (!(type & (ZEROPAD + LEFT)))
- while (size-- > 0)
- *str++ = ' ';
- if (sign)
- *str++ = sign;
- if (type & SPECIAL) {
- if (base == 8)
- *str++ = '0';
- else if (base == 16) {
- *str++ = '0';
- *str++ = ('X' | locase);
- }
- }
- if (!(type & LEFT))
- while (size-- > 0)
- *str++ = c;
- while (i < precision--)
- *str++ = '0';
- while (i-- > 0)
- *str++ = tmp[i];
- while (size-- > 0)
- *str++ = ' ';
- return str;
- }
- int vsprintf(char *buf, const char *fmt, va_list args)
- {
- int len;
- unsigned long num;
- int i, base;
- char *str;
- const char *s;
- int flags;
- int field_width; /*位宽输出*/
- int precision;
- int qualifier;
- //这里判断,如果在字符串fmt中不存在%这个符号,那么字符串继续往后遍历
- for (str = buf; *fmt; ++fmt) {
- if (*fmt != '%') {
- *str++ = *fmt;
- continue;
- }
- //程序设置标志位
- flags = 0;
- repeat:
- ++fmt; /* this also skips first '%' */
- //格式控制
- 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;
- }
- //获取宽度,这里主要是要实现printf的位宽机制
- field_width = -1;
- if (isdigit(*fmt))
- field_width = skip_atoi(&fmt);
- else if (*fmt == '*') {
- ++fmt;
- field_width = va_arg(args, int);
- if (field_width < 0) {
- field_width = -field_width;
- flags |= LEFT;
- }
- }
- precision = -1;
- if (*fmt == '.') {
- ++fmt;
- if (isdigit(*fmt))
- precision = skip_atoi(&fmt);
- else if (*fmt == '*') {
- ++fmt;
- precision = va_arg(args, int);
- }
- if (precision < 0)
- precision = 0;
- }
- /*得到的转换限定符*/
- qualifier = -1;
- if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
- qualifier = *fmt;
- ++fmt;
- }
- /*默认进制为10进制*/
- base = 10;
- //以下主要是要实现printf的格式输出 例如:%d , %c , %u ...等等
- switch (*fmt) {
- case 'c': //以字符形式进行输出
- if (!(flags & LEFT))
- while (--field_width > 0)
- *str++ = ' ';
- *str++ = (unsigned char)va_arg(args, int);
- while (--field_width > 0)
- *str++ = ' ';
- continue;
- case 's': //以字符串形式进行输出
- s = va_arg(args, char *);
- len = strnlen(s, precision);
- if (!(flags & LEFT))
- while (len < field_width--)
- *str++ = ' ';
- for (i = 0; i < len; ++i)
- *str++ = *s++;
- while (len < field_width--)
- *str++ = ' ';
- continue;
- case 'p': //以地址形式输出,也就是以16进制数输出
- if (field_width == -1) {
- field_width = 2 * sizeof(void *);
- flags |= ZEROPAD;
- }
- str = number(str,
- (unsigned long)va_arg(args, void *), 16,
- field_width, precision, flags);
- continue;
- case 'n':
- if (qualifier == 'l') {
- long *ip = va_arg(args, long *);
- *ip = (str - buf);
- } else {
- int *ip = va_arg(args, int *);
- *ip = (str - buf);
- }
- continue;
- case '%': //这里表示字符串中存在%号这个字符
- *str++ = '%';
- continue;
- /* integer number formats - set up the flags and "break" */
- case 'o': //%o 表示8进制输出
- base = 8;
- break;
- case 'x': //%x或者%X 表示16进制输出
- flags |= SMALL;
- case 'X':
- base = 16;
- break;
- case 'd': //%d %i整形数输出,%u无符号整形
- case 'i':
- flags |= SIGN;
- case 'u':
- break;
- default:
- *str++ = '%';
- if (*fmt)
- *str++ = *fmt;
- else
- --fmt;
- continue;
- }
- if (qualifier == 'l') //以无符号长整型输出
- num = va_arg(args, unsigned long);
- else if (qualifier == 'h') {
- num = (unsigned short)va_arg(args, int);
- if (flags & SIGN)
- num = (short)num;
- } else if (flags & SIGN)
- num = va_arg(args, int);
- else
- num = va_arg(args, unsigned int);
- str = number(str, num, base, field_width, precision, flags);
- }
- *str = '\0'; //字符串遍历到有\0的地方就停止
- return str - buf;
- }
- //可变参形式实现sprintf
- int mysprintf(char *buf, const char *fmt, ...)
- {
- va_list args;
- int i;
- va_start(args, fmt);
- //将获取到的fmt格式字符串写入到buf这个缓存里去
- i = vsprintf(buf, fmt, args);
- //释放args
- va_end(args);
- return i;
- }
- //可变参形式进行实现myprintf
- int myprintf(const char *fmt, ...)
- {
- char printf_buf[1024];
- va_list args;
- int printed;
- va_start(args, fmt);
- printed = vsprintf(printf_buf, fmt, args);
- va_end(args);
- puts(printf_buf);
- return printed;
- }
- int main(void)
- {
- myprintf("输出字符串:hello world!\n") ;
- static int sum , a = 3 , b = 4;
- sum = a + b ;
- myprintf("sum(十进制输出):%d\n",sum);
- myprintf("sum(16进制输出):%p\n",sum);
- char buffer[128] = {0};
- //将字符串存到一个数组buffer里去
- mysprintf(buffer , "输出字符串:hello world!\n");
- //以字符串格式输出这个buffer的内容
- myprintf("buffer:%s\n",buffer);
- return 0 ;
- }
看完代码就知道了,我们这个程序没有包含stdio.h这个头文件,一样也就实现了printf和sprintf这两个函数。这样的话,以后如果要自己实现一个printf函数,这份代码就可以作为一个文件来进行调用了,哈哈!