printf 从现象到本质

本文探讨了printf函数的%s格式输出特性,特别是在强制类型转换场景下的表现。通过源码分析,揭示了strnlen函数如何处理字符串结束符,以及在没有正确结束符时可能引发的问题。文章鼓励读者通过阅读Linux源码来深入理解基础函数的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

讲真,要深入理解 printf,只能自己看源码。

更精彩内容,可以关注我的博客:wenfh2020.com


从现象到本质

前些时间,朋友问了一个问题:

printf%s 格式输出,如果参数是其它类型的数据强制转换为 char* 的,结果会怎么样?

我想最好的方法莫过于马上动手测试一下,看看结果。如果再问:printf 是线程安全的吗?… 问题很多,这些都只是问题的表象,要从现象看本质。Linux 是开源的,何不看源码理解程序工作原理呢?


从源码上看 strnlen 是通过查找内存的 ‘\0’ 字符串结束符的。如果强制转换的内存数据,没有 ‘\0’ 结束符,那 strnlen 就会出现问题。

/* https://github.com/torvalds/linux/blob/master/arch/x86/boot/printf.c */

int printf(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 vsprintf(char *buf, const char *fmt, va_list args) {
    ...
    switch (*fmt) {
        ...
        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;
    }
    ...
}

/* https://github.com/torvalds/linux/blob/master/arch/x86/boot/string.c */

size_t strnlen(const char *s, size_t maxlen) {
    const char *es = s;
    while (*es && maxlen) {
        es++;
        maxlen--;
    }

    return (es - s);
}

总结

不只 printf 的实现,很多基础函数,都能从 linux 源码中查看。可以从 github 下载一份源码,方便查看。下载个 zip 文件包吧,git 文件有点太大了。

/* https://github.com/torvalds/linux/blob/master/lib/string.c */

char *strcpy(char *dest, const char *src) {
    char *tmp = dest;

    while ((*dest++ = *src++) != '\0')
        /* nothing */;
    return tmp;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值