【printf】搞清楚printf重定位

调用链(典型 newlib / arm-none-eabi 编译器)

printf(...) 
  -> vfprintf(...) 
    -> fwrite/fputc(...)  // 将数据写到 stdout 的缓冲区
      -> 当需要向设备输出(缓冲满或 flush)时
        -> 底层调用系统写函数 _write / sys_write / _write_r
          -> 你在 syscalls.c / retarget.c 中提供的实现被调用
            -> 硬件驱动(UART transmit / tud_cdc_write 等)

 

重要:fputc() 只是写入 FILE 流,只有在 flush / buffer full / 行缓冲遇到 \n(交互终端),或显式调用 fflush() 时,才会触发 _write()

 

fputc_write_write_r 之间的关系与差异

  • fputc(int ch, FILE *f):标准库函数,用于写单个字符到 FILE 流(例如 stdout)。它是 C 标准库的一部分。

  • _write(int fd, const void *buf, int len)(或 int _write(int file, char *ptr, int len)):newlib / syscalls 要实现的函数,用于将 fd(通常 0 stdin, 1 stdout, 2 stderr)上的数据写到底层设备。实现它就是重定向 printf 的核心方法。

  • _write_r(struct _reent *r, int fd, const void *buf, int len):newlib 的可重入版本(在使用 reentrant newlib 时)。如果你的 toolchain 使用 reentrant syscalls,需要实现 _write_r 而不是 _write

  • sys_write:有些工程用这个名字替代 _write,再通过宏或链接把它和 _write 绑定。

 

为什么直接重写 fputc() 有时无效或不稳定

  • fputc() 写的是 FILE 的缓冲区;如果库/运行时没有立刻 flush(例如你在行缓冲之外、或者 stdout 不是交互式终端),数据可能不会立刻通过 _write() 发出。

  • 标准库内部实现很复杂:printf/vfprintf 可能用 fwrite() 绕过 fputc() 的逐字符路径(一次性写大块),所以单独覆盖 fputc() 不一定能拦截所有 printf 输出。

  • 因此推荐直接实现 _write(),这是 newlib 设计用于替代操作系统 write 的入口,能统一所有 stdout/stderr 输出。

 

posted @ 2025-10-31 15:29  壹点灵异  阅读(12)  评论(0)    收藏  举报