博客网址:www.shicoder.top
微信:18223081347
欢迎加群聊天 :452380935
这一次我们来实现最基础,也是最常见的函数print
,大家都知道这个是可变参数函数,那具体怎么实现呢,我们慢慢来说吧
大家都知道我们常见的格式化输出函数printf
里面有很多参数,比如
%[flags][width][.prec][h|l|L][type]
%
:格式引入字符flags
:可选的标志字符序列width
:可选的宽度指示符.prec
:可选的精度指示符h|l|L
:可选的长度修饰符type
:转换类型
首先来说下变参是怎么实现的,其实变参和三个参数有关
va_list
:保存可变参数指针va_start
:启用可变参数va_arg
:获取下一个参数va_end
:结束可变参数
这里我们是宏函数实现
typedef char *va_list; // 保存可变参数
// 因为是将参数全部压栈,最后一个压栈的是参数的个数,所以只需要挨着在内存中找多少个就知道了
#define va_start(ap, v) (ap = (va_list)&v + sizeof(char *)) // 启用可变参数 指向v下一个参数地址
#define va_arg(ap, t) (*(t *)((ap += sizeof(char *)) - sizeof(char *))) // 获取下一个参数 并将值转换为t格式
#define va_end(ap) (ap = (va_list)0) // 结束可变参数
基本上懂了这个原理,就可以基本实现printf
函数,只不过这个函数多了一些格式化字符串的参数。先直接看下我们要最终得到什么
void kernel_init()
{
console_init();
int cnt = 30;
while (cnt--)
{
printk("hello system %#010x\n", cnt);
}
}
其实就是输出30个十六进制数,占10位,不足前面用0填充,结果如下
那么就是如何实现这个printk
了,先看下它的代码
static char buf[1024];
int printk(const char *fmt, ...)
{
va_list args;
int i;
// 此时的args就是fmt下一个参数的地址
va_start(args, fmt);
// 将内容格式化到buf里面
i = vsprintf(buf, fmt, args);
va_end(args);
// 写到控制台上
console_write(buf, i);