printf 内部原理和实现 (你想知道的C语言 1.2)

Q: 已经有write系统调用, 为何还需要printf?

A: write系统调用作为操作系统API,为用户层提供写文件最基本的实现.

     printf作为libc的基本函数之一, 为c语言级别标准, 可以让用户层抛开操作系统write系统调用的差异,

   提供一套带有缓冲机制的输出功能. 换句话说, c++, java等语言同样有类似的"printf", 最终一定会调用write系统调用.

     需要在标准输出读写时, 我们可以不调用printf, 但write是一定会使用,我们还可以在write基础上再封装

   一套新的"printf"实作, 作为自定义的printf.

 

Q: 给一个简单的printf实作实例.

A: 对于标准C语言, printf不像write那样,传递的参数比较单纯, printf需要处理不同的格式串, 缓冲区类型.

#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>

char *itoa(int i, char *buf)
{
    int j = 0;
    int k;

    do {
        k = i % 10;
        buf[j++] = k + '0';

        i /= 10;
    } while(i);

    buf[j] = '\0';

    for (k = 0; k < j / 2; ++k) {
        char temp = buf[k];
        buf[k] = buf[j - 1 - k];
        buf[j - 1 - k] = temp;
    }
    
    return buf;
}

int simple_printf(char *fmt, ...)
{
    static char buf[128];
    va_list ap;
    char *p;
    int val, i = 0;
    char c;
    ssize_t cnt = 0, total_cnt = 0;
    char str[16];
    
    memset(buf, 0, 128);
    va_start(ap, fmt); 
    for (p = fmt; *p; p++) {
        if (*p != '%') {
            buf[i++] = *p;  // add characters to output buffer
            ++cnt;
            if (*p == '\n') {   // new line, output all chars
                cnt = write(0, buf, cnt);
            }
            continue;
        }
        switch (*++p) {
        case 'd':
            val = va_arg(ap, int);
            itoa(val, str);
            strcat(buf + i, str);
            cnt += strlen(str);
            i += strlen(str);
            break;
        case 'c':
            val = va_arg(ap, int);
            c = (char)val;
            buf[i++] = c;   // add characters to output buffer
            cnt++;
            break;
        default:
            buf[i++] = *p;  // add characters to output buffer
            cnt++;
            break;
        }
    }

    va_end(ap);

    return cnt;
}

int main(int argc, char *argv[])
{
    int ret;

    ret = simple_printf("hello%d\n", 1);
    simple_printf("%d\n", ret);

    return 0;
}

上面的代码简单的展示了基本的printf实现流程.

1 缓冲区;

2 行缓冲;

3 格式%d或者%c解析;

4 调用write系统调用完成实际输出.

当然,细心的读者可以看到这个实现依然有不少异常处理/多线程问题没有处理, 有兴趣可以修正它!

 

附注: Apple libc源代码:  https://opensource.apple.com/source/Libc/Libc-1272.250.1/

对于write系统调用内部的实现:  write系统调用的实现

 

作者:     陈曦
环境:     MacOS 10.14.5
         Apple LLVM version 10.0.1 (clang-1001.0.46.4)
         Target: x86_64-apple-darwin18.6.0
 
转载请注明出处
  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值