Linux 源码系列之可变参数列表实现

背景

从事 android 开发四年有余,应用开发做得越久,就越有“知其然不知其所以然”的感觉,于是乎,过去的大半年,我几乎一有时间就去啃《Linux内核完全剖析-基于0.12内核》,接近1000页的 linux 系统源码,读的过程可谓是五味杂陈,终于在2016春节跟它作了一个了断。虽不能说对 linux 的底层实现已了然于心,但阅读这本书确实起到了醍醐灌顶的功效,在很大程度上打通了我在技术上的任督二脉。

有人会问,为什么是0.12版本?

答案很简单:

麻雀虽小,五脏俱全。

恕我才疏学浅,就像我更喜欢看 2.3版本的 android framework 源码一样,就是因为简单。linux 发展至今20余年,历经茫茫多的版本迭代,但其中的设计思想却是历久弥新,跟动辄几百万行的最新版本源码相比,先选一个软柿子捏捏,何乐而不为呢。

接下来我会把书上看到的知识点结合我自己的理解,写成一个系列发布出来,也算是对自己过去一段时间学习成果的整理和总结。

可变参数列表的使用

可变参数列表在很多语言中都有对应的语法支持,比如 Javapython。C 语言虽然是一门低级语言, 但是也支持可变参数列表, 而且它的可变参数列表实现得简单又不失巧妙,其中最著名的就是几个用来格式化字符串输出的 C 标准函数:

  • printf, 直接把输出送到标准输出句柄 stdout
  • cprintf, 把输出送到控制台
  • fprintf, 把输出送到文件句柄
  • sprintf, 把输出送到以 null 结尾的字符串中

可变参数列表的实现

下面就以 sprintf 函数说明在 C 标准库函数中是如何实现可变参数列表的。在说明 sprintf的实现之前,非常有必要温习一下 C 函数调用在操作系统中是如何实现的,后面你们就知道为什么这里说这个很重要了。

C 函数调用机制

学过计算机的人都有一个模糊的印象(如果能把这里的事情说明白,那你基础一定很扎实),函数调用是通过栈来实现的,基本上就是一个入栈出栈的过程。大家可能又知道,C 的函数调用是传值调用,意思就是说 C 函数中用到的参数只是函数调用时传入参数的一个副本,所以要修改某个变量,就必须传入对应变量的地址指针。

那么为什么是这样的?且看下面这幅图

这里写图片描述

上面这幅图描绘了一个典型的函数调用栈内存结构,其中栈帧是指单个函数调用所使用的内存部分。每个栈帧的起始位置保存在寄存器 ebp 中。当在 C 程序中做函数调用的时候,比如函数 A 调用函数 B,在 A 的代码逻辑中会把 B 函数用到的实参压入栈中,实参所在的内存部分实则属于 A 函数的栈帧部分(参数1到参数n),所以图中参数1到参数n部分其实是在函数 B 被调用之前被复制到内存中的,即所谓的副本。

问题来了,我们知道C 程序里仅仅是一个简单的函数调用,那么这部分工作是由谁来完成的呢?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值