这个开源printf它到底是个啥?printf,顾名思义就是把格式化好的字符串“喊”出来,传统的 stdio.h 实现太肥——在资源紧张的单片机上会把代码体积膨胀 20 KB 甚至更高。于是社区的 mpaland/printf 横空出世:只要把 printf.c 丢进项目,配上一个 _putchar(),立马拥有完整的 printf / sprintf / snprintf / fctprintf 功能,且 不依赖任何库,代码只有几百行,体积小到让人惊呼。
它解决了哪些痛点?
|
痛点 |
传统方式 |
这款 printf 的解决方案 |
|
代码体积爆炸 |
链入完整 libc,几十 KB |
仅 600 行源码,几 KB 代码 |
|
线程安全/可重入性差 |
静态缓冲区、全局变量 |
完全不使用静态变量,随调用即安全 |
|
浮点/长整支持不完整 |
需要额外库或手动移植 |
支持 |
|
依赖外部库 |
必须链接 libc,增加维护成本 |
零依赖,直接编译进项目 |
|
调试不友好 |
打印信息受限,格式不全 |
完整的 flag、宽度、精度、长度修饰符,几乎兼容标准 C99 |
怎么装、怎么用?
-
1. 下载源码:
git clone https://github.com/mpaland/printf.git,把printf.c、printf.h拉进你的工程。 -
2. 实现低层输出:在项目里写个
_putchar(),把字符发到 UART、SWO 或者 LCD。void _putchar(char ch)
{
// 这里把字符写到串口
UART_SendByte(ch);
}
3. 像使用标准库一样调用:
printf("温度=%0.2f°C, 计数=%d\r\n", temperature, cnt);
sprintf(buf, "ID:0x%X", device_id);
snprintf(buf, sizeof(buf), "电压=%3.1fV", voltage);
fctprintf(my_stream, NULL, "实时日志: %s", log);
4. 可选宏:如果真的要极限压缩,可以在编译时加 -DPRINTF_DISABLE_SUPPORT_FLOAT、-DPRINTF_DISABLE_SUPPORT_LONG_LONG 等,去掉不需要的特性。
优缺点一目了然
|
优点 |
说明 |
| 体积小 |
只要几 KB,适配所有资源受限的 MCU |
| 零依赖 |
不需要 libc、new、malloc,直接编进裸机项目 |
| 功能全 |
完整的格式化支持(%d、%x、%f、%e、%b、%p…) |
| 线程安全 |
没有全局状态,随调用即安全 |
| 可裁剪 |
通过宏关闭浮点、长整、指数等特性 |
| MIT 许可证 |
商业项目随意使用,无侵权顾虑 |
|
缺点 |
说明 |
| 需要自行实现 _putchar |
对新手来说多了一步底层适配 |
| 不支持宽字符 |
只处理普通 |
| 调试信息有限 |
只能输出到实现的接口,若接口本身有问题会“看不见”日志 |
总结一下
如果你在做 STM32、ESP32、Raspberry‑Pi Pico 之类的嵌入式项目,别再硬搬 libc 的 printf,那等于是把体积炸弹装进你的固件。mpaland/printf 用几百行代码把完整的格式化功能搬到了裸机环境,零依赖、可裁剪、线程安全——简直是嵌入式开发者的福音。把它拉进项目,省的每次想打印调试信息时都要去翻库文档,直接写 printf("x=%d\r\n", x);,省时省力又省空间。
项目地址:https://github.com/mpaland/printf