linux ksprintf源码,C语言之linux内核可变参实现printf,sprintf(示例代码)

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

mad.gif

mad.gif

#include

#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 ZEROPAD1/* pad with zero */

#define SIGN2/* unsigned/signed long */

#define PLUS4/* show plus */

#define SPACE8/* space if plus */

#define LEFT16/* left justified */

#define SMALL32/* Must be 32 == 0x20 */

#define SPECIAL64/* 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函数,这份代码就可以作为一个文件来进行调用了,哈哈!

laugh.gif

dd9390fc70d34c1083906cb2910c9aae.jpg

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值