《C Programming Language》第二版书中printf的最小实现详细解析

详细解析 minprintf 的最小实现

Link: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628
翻看这本书,找寻printf的实现,书中给出一个最小实现,下面解析之。
在这里插入图片描述

这是C语言之父写的书

丹尼斯·麦卡利斯泰尔·里奇(英语:Dennis MacAlistair Ritchie,1941年9月9日—2011年10月12日),美国计算机科学家。黑客圈子通常称他为“dmr”[4]。他是C语言的创造者、Unix操作系统的关键开发者[5][6][7][8],对计算机领域产生了深远影响,并与肯·汤普逊同为1983年图灵奖得主。https://zh.wikipedia.org/wiki/%E4%B8%B9%E5%B0%BC%E6%96%AF%C2%B7%E9%87%8C%E5%A5%87

代码展示了一个简化版的 printf 函数实现,名为 minprintf,能够处理有限的格式化输出功能,包括整数 (%d)、浮点数 (%f) 和字符串 (%s)。以下是对代码的逐步解析,并特别关注 *++p 的语法。


代码核心功能
void minprintf(char *fmt, ...) {
    va_list ap; 
    char *p, *sval;
    int ival;
    double dval;

    va_start(ap, fmt); // 初始化可变参数列表
    for (p = fmt; *p; p++) { // 遍历格式化字符串
        if (*p != '%') { // 如果不是格式说明符,直接输出字符
            putchar(*p);
            continue;
        }
        switch (*++p) { // 获取格式说明符
            case 'd':
                ival = va_arg(ap, int); // 提取 int 参数
                printf("%d", ival);
                break;
            case 'f':
                dval = va_arg(ap, double); // 提取 double 参数
                printf("%f", dval);
                break;
            case 's':
                for (sval = va_arg(ap, char *); *sval; sval++) // 输出字符串
                    putchar(*sval);
                break;
            default:
                putchar(*p); // 如果不是已知格式说明符,直接输出
                break;
        }
    }
    va_end(ap); // 清理可变参数
}

重点语法解析:*++p

在代码中,*++p 是一个关键的语法结构,其作用是:

  1. ++p:将指针 p 向前移动一个字符,指向下一个位置。
  2. *++p:解引用新位置的字符,即获取格式说明符。

完整流程

  • 如果当前字符为 '%',通过 ++p 跳过 '%',检查其后面的格式说明符(如 'd''f''s' 等)。
  • 解引用 *++p 获取格式说明符,用于 switch 语句处理。

示例与分析

假设有如下调用:

minprintf("Value: %d, Name: %s, Score: %f\n", 42, "Alice", 95.5);

1. 处理流程

  • p = "Value: %d, Name: %s, Score: %f\n"fmt 的内容)
  • 遍历字符串,遇到普通字符(如 'V''a' 等)时,直接输出。
  • 遇到 '%' 时,调用 *++p 获取后续格式说明符并处理。

2. *++p 语法的作用

  • p 指向 '%' 时:
    • ++p 将指针移动到下一位置,即 'd'
    • *++p 解引用,返回 'd',交由 switch 处理。
  • 以此类推,后续会分别处理 '%s''%f'

3. 每一步指针状态

  • 起始:p 指向 V,输出 V,指针移动。
  • 遇到 %:调用 *++p,跳过 %,获取下一个字符 'd'
  • 解析 d 后输出 42,继续处理后续字符。

示例输出分析

对于上述代码调用:

minprintf("Value: %d, Name: %s, Score: %f\n", 42, "Alice", 95.5);

步骤

  1. 输出普通字符:Value:
  2. 遇到 %d
    • *++p 解引用,获取 d
    • 提取整数 42,格式化输出为 42
  3. 输出普通字符:, Name:
  4. 遇到 %s
    • *++p 解引用,获取 s
    • 提取字符串 "Alice",逐字符输出为 Alice
  5. 输出普通字符:, Score:
  6. 遇到 %f
    • *++p 解引用,获取 f
    • 提取浮点数 95.5,格式化输出为 95.500000(默认 6 位小数)。
  7. 输出换行符 \n

最终输出

Value: 42, Name: Alice, Score: 95.500000

总结
  • *++p 的作用
    • ++p 移动指针到下一字符。
    • *++p 解引用获取当前字符,用于格式说明符处理。
  • 代码实现思路
    • 遍历格式化字符串,按字符解析。
    • 对于普通字符,直接输出。
    • 遇到 % 时,通过 *++p 获取说明符,并提取参数处理。
  • 关键点
    • va_list 的使用使得 minprintf 能够处理可变参数。
    • *++p 高效实现格式说明符解析,是简化实现的核心。

这段代码简单直观,体现了 C 语言实现格式化输出的核心思想,是理解 printf 工作原理的重要案例。

后记

2025年1月27日于山东日照。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值