• 正文
  • 相关推荐
申请入驻 产业图谱

一道联发科面试题:printf 为什么可以传任意多个参数

16小时前
202
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是仲一。

联发科一面,面试官问了一个看起来很简单的问题:

"printf 可以传 1 个参数,也可以传 10 个参数。它是怎么做到的?"

这道题表面考 printf,实际考的是 C 语言可变参数和栈帧。

从 printf 的原型说起

int printf(const char *format, ...);

... 就是 C 语言的可变参数语法。一个函数只要在参数列表最后加上 ...,就可以接受不定数量的参数。

但问题来了:被调用的函数怎么知道传了多少个参数?怎么拿到这些参数?

可变参数的工作原理

先看一个关键事实:C 语言调用函数时,参数是从右往左依次压栈的。最后一个参数最先入栈,第一个参数最后入栈,并且靠近栈顶。

所以对 printf 来说,第一个确定的参数 format 必然在栈上固定位置。从 format 的地址向高地址方向,依次排列着后续所有参数。

拿到 format 之后,printf 通过解析 %d%s 等格式控制符来确定后面有几个参数、每个参数是什么类型。解析一个格式,就从栈上取一个参数。

这个过程 C 标准库提供了一组宏来封装

va_list ap;          // 声明一个变参列表
va_start(ap, last);  // 初始化,ap 指向 last 后面的第一个参数
va_arg(ap, type);    // 取一个参数,类型是 type,自动移动指针
va_end(ap);          // 清理

简化理解:va_start 算好第一个可变参数的地址,va_arg 每次取一个并把指针向后移动 sizeof(type) 个字节。

如果 format 和参数不匹配会发生什么

这是变参函数最大的风险。如果 format 写的是 %d,实际传的是一个 long long,va_arg 拿到的数据就是错的——因为它按 int 大小(4 字节)移动指针,但实际数据是 8 字节,后面所有参数的位置都偏了。

printf 不会帮你检查这些。编译器能检查出部分类型不匹配(启用了 -Wformat 的话),但不是所有情况都能发现。

自己实现一个简单的变参函数

看完原理后,可以自己写一个:

#include <stdarg.h>

void my_print(const char *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int val = va_arg(ap, int);
    printf("value: %dn", val);
    va_end(ap);
}

当然这只是演示,实际的 my_print 要像 printf 一样解析格式化字符串才能处理多个参数。

嵌入式场景

嵌入式调试中,我们自己写的串口打印函数,有时也用可变参数封装:

void uart_printf(const char *fmt, ...) {
    char buf[256];
    vsnprintf(buf, sizeof(buf), fmt, va_list);
    uart_send(buf);
}

vsnprintf 接受 va_list 参数,配合 va_start / va_arg 机制,就能把可变参数转成格式化后的字符串,再从串口发出去。


你被问过 printf 相关的问题吗?留言聊聊。

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

作者就职于某500强公司,担任BSP工程师。具有丰富的嵌入式开发经验。专栏主要分享计算机基础,操作系统,Linux驱动开发,Arm体系与架构,C/C++,数据结构与算法等相关文章。欢迎关注我的公众号【嵌入式与Linux那些事】,一起学习交流。