7.6实现字符串的格式化输出

7.6实现字符串的格式化输出

log_printf("Version: %s", OS_VERSION);

后面是可变参数,怎么接收这个可变参数?

C语言stdarg.h库的学习

stdarg.h简介

stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。

可变参数的函数通在参数列表的末尾是使用省略号(,…)定义的

va_list
这是一个适用于 va_start()、va_arg()va_end() 这三个宏存储信息的类型。

序号宏&描述
1 void va_start(va_list ap, last_arg) 这个宏初始化 ap 变量,它与 va_argva_end 宏是一起使用的。last_arg 是最后一个传递给函数的已知的固定参数,即省略号之前的参数。
2type va_arg(va_list ap, type) 这个宏检索函数参数列表中类型为 type 的下一个参数。
3 void va_end(va_list ap) 这个宏允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。
void va_start(va_list ap, xxx v) /* 其中的 xxx 为任意类型变量 */
{
    ap = (va_list)&v + sizeof(v); //第一个可选参数地址
}
xxx va_arg(va_list ap, xxx t) /* 其中的 xxx 为任意类型变量 */
{
    // (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))  
    ap += sizeof(t); //下一个参数地址
    return *((t *)(ap - sizeof(t))) //第一次调用返回的就是第一个参数的值
   	
}
void va_end(va_list ap) 
{
    ap = (va_list)0; //强制转换,结尾v这个特殊的数组清零
}

== 这个库函数的底层也是利用了形参在函数栈中的位置(这是固定的,之前16位实模式向32位保护模式传信息也是靠这个方法来实现的),来取出形参==

如何判断输入的形参结束了,最后那个fmt字符串走到了\0,形参输出完毕

格式化字符串函数

将格式化后的完整字符串放到char数组中

void kernel_vsprintf(char * buffer, const char * fmt, va_list args) {
    enum {NORMAL, READ_FMT} state = NORMAL;
    char ch;
    char * curr = buffer;
    while ((ch = *fmt++)) {
        switch (state) {
            // 普通字符
            case NORMAL:
                if (ch == '%') {
                    state = READ_FMT;
                } else {
                    *curr++ = ch;
                }
                break;
            // 格式化控制字符,只支持部分
            case READ_FMT:
                if (ch == 's') {
                    const char * str = va_arg(args, char *);
                    int len = kernel_strlen(str);
                    while (len--) {
                        *curr++ = *str++;
                    }
                }
                state = NORMAL;
                break;
        }
    }
}

这个函数设计蛮巧妙的

日志打印函数带格式化输出的改进

void log_printf(const char * fmt, ...) {
    char str_buf[128];
    va_list args;

    kernel_memset(str_buf, '\0', sizeof(str_buf));

    va_start(args, fmt);
    kernel_vsprintf(str_buf, fmt, args);//将字符串格式化到一个缓冲区里面
    va_end(args);

    const char * p = str_buf;
    while (*p != '\0') {
        while ((inb(COM1_PORT + 5) & (1 << 6)) == 0);   //看串行接口是不是处于忙的状态
        outb(COM1_PORT, *p++);
    }

    outb(COM1_PORT, '\r'); //换行,改变了列号变为0
    outb(COM1_PORT, '\n');//回车,改变了行号变为下一行行号
}

测试

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值