| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> C++知识库 -> 深入浅出c语言可变参数函数 -> 正文阅读 |
|
[C++知识库]深入浅出c语言可变参数函数 |
????????C语言可变参数函数,有很多介绍。本文只想从C语言和编译器角度,借助一点逻辑思考的方式,深入浅出理解一下可变参数函数的实现。 ????????说到可变参数函数,最先想到的就是int printf(const char *format, ...)。刚开始看到可变参数函数感觉特别神奇,为什么这个函数就可以传入的参数就可以是任意个,类型也是任意的。对于固定参数的函数,形参,实参是一一对应的,调用函数的时候,参数一一传递进去,十分容易理解,但是对于可变参数呢,试想你在调用printf传入5个参数,那么printf在执行的时候,它要是想正确的执行你的调用,它必须有办法知道你输入的参数的个数和参数类型,这是关键中的关键。从表面上看printf没有办法知道啊,因为参数不是固定的,它是被调用方,它如何知道调用者输入了几个参数呢。让我们举例来分析:
注意第一个输入参数,是一个字符串"int?= %d, c?= %c\n, s = %s",且称它为*fmt。作为一个C语言的使用者,光从这个字符串,我们很容易推导出,这个printf 有四个输入参数,第一个就是字符串,第二个是整型,第三个是字符型,第四个是字符串。是的,这是我们通过第一个字符串自己推导出来的。因为我们作为使用者知道%是个特殊字符,它和某些字符的结合,如%d,%s,%c,就能确定后面必然有对应的参数输入。好了,既然我们能推导出来,那么计算机自然也会推导的。printf只需要对第一个字符串*fmt进行解析,找出特殊符号组合,根据规则,就可以确定,函数后续输入参数的个数及其类型,这个并不难,就是分析字符串。与固定参数函数的区别在于,对于可变参数函数,想要得到参数,需要自己分析。 ????????当然还有其他类型的可变参数函数,如int sum(int count, ...);这个函数可以计算任意个数据之和。可能有人会说,这个和printf不一样,它的第一个参数是个整数,如何确定参数个数和类型。注意了,这个函数的第一个参数,表示后续输入参数的个数,至于参数的类型,那是默认的int型。这样参数的类型和数据都知道了。 ????????当然,可变参数还要有很多形式,但是有一点可以肯定,就是参数的数量和个数,要么通过函数的一个参数传递进去,要么就是针对该函数有一套约定的默认规则,函数根据传递的参数或者约定的规则,最终完成参数的解析。 ? ? ? ? 说到这里,下一个问题来了,既然知道参数的个数和类型了,怎的得到参数的值呢?这个问题比较复杂,这里我不作展开。对于不同的cpu架构,不同的编译器,底层传参操作遵循的规则是不一样的。比如对于arm的apcs规则,参数数量较少,使用r0~r3来传递参数,参数太多,后续的会使用stack空间来传递参数。而对于x86架构,很明显是没有r0~r3的。幸运的是,一般编译器都帮我们编写好了用于定位参数,取出参数需要用的函数,它们一般定义在头文件<stdarg.h>中:
上述函数通常用宏来实现,例如标准ANSI形式下,这些宏的定义是:
具体可以参考《C语言可变参数函数详解示例》的介绍。如果需要更深入了解底层的实现可参见《亲密接触C可变参数函数》。 这里我们以一个例子来说明这些函数的使用方法:
执行结果如下:
这个函数要实现一个可变参数的函数,第一个传入字符串,用于定义后续参数的个数和类型,例中“dcsd”,表示,后面有四个参数,分别是整型,字符型,字符串型,整型。 函数首先调用va_start();将ap指针指向后续第一个输入参数;然后foo函数开始了自己分析参数的过程:
最后重置ap指针——va_end(ap)。 ? ? ? ? 如果把函数的调用者比作领导,函数本身比作下属。那么,一般函数的领导,下命令就是逐条列出,思路清晰。而可变参数函数的领导,下的命令就是一锅大杂烩,需要下属自己去抽丝剥茧,逐条理清,或者是命令过于简单,需要下属自己根据日常规则去领悟。 ? ? ? ? 总之,理解可变参数函数就两条: 1.如何确定输入参数的个数,以及对应的类型。这个需要函数自己根据规则去分析——自己动手,丰衣足食。 2.如何获取参数的值。必须的底层函数,编译器帮忙封装好了,能做的就是合理调用——按部就班,他山之石,可以攻玉。 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/23 23:59:18- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |