C 语言精髓之变参函数

我们以 printf 这个 very 熟悉的函数为例,来分析一下变参函数。先看下 printf 函数的定义:

int printf(const char *fmt, ...){    int i;    int len;    /* va_list 即 char * */va_list args;va_start(args, fmt);    /* 内部使用了 va_arg() */len = vsprintf(g_PCOutBuf,fmt,args);va_end(args);    for (i = 0; i < strlen(g_PCOutBuf); i++){putc(g_PCOutBuf[i]);}    return len;
}


什么是变参函数?

可变参数函数的原型声明为 type VAFunction(type arg1, type arg2, … );

参数可以分为两部分:个数确定的固定参数和个数可变的可选参数。函数至少需要一个固定参数,固定参数的声明和普通函数一样;可选参数由于个数不确定,声明时用 "..." 表示。固定参数和可选参数共同构成一个函数的参数列表。


printf 函数涉及了以下几个重要的宏:

typedef char *   va_list;/** Storage alignment properties*/#define _AUPBND (sizeof (acpi_native_int) - 1) //acpi_native_int 为 4 字节(根据机器字长而定)#define _ADNBND (sizeof (acpi_native_int) - 1) /** Variable argument list macro definitions*/#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))#define va_end(ap) (void) 0#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))


分析以下三个宏的作用


1) va_start 宏

作用: 根据 A 取得可变参数表的首指针并赋值给 ap。

原理: 根据最后一个固定参数 A 的地址 + 第一个变参对 A 的偏移地址,然后赋值给 ap,这样 ap 就是可变参数表的首地址(函数传递的参数会从右向左依次入栈,并且 ARM 的栈为降栈,所以参数 A 的地址最低)。


2) va_arg 宏

作用: 指取出当前 ap 所指的可变参数并将 ap 指针指向下一可变参数。


3) va_end 宏

作用: 结束可变参数的获取,与 va_start 对应使用。

堆栈中,各个函数的分布情况是倒序的,即最后一个参数在列表中地址最高部分,第一个参数在列表地址的最低部分。参数在堆栈中的分布情况如下:*******************最后一个参数  倒数第二个参数 ...          第一个参数     函数返回地址   函数代码段     *******************得到可变参数个数的三种办法:1) 函数的第一个参数,指定后续的参数个数,如 func(int num,...);2) 根据隐含参数,判断参数个数,如 printf 系列的,通过字符串中 % 的个数判断;3) 特殊情况下(如参数都是不大于 0xFFFF 的 int),可以一直向低处访问堆栈,直到返回地址。

而 _bnd(X, bnd) 宏就是以 4 字节对齐的变量 X 的大小。

自己实现一个简单的 printf 函数

#include #include #include #include void print(char *fmt, ...){va_list ptr;va_list temp_ptr = NULL;    /* 用于存储最终转换的结果 */char buf[100] = {0};    /* 用于存储临时转换的结果 */char temp_buf[50] = {0};    unsigned char index = 0;    unsigned char len, arg_len;len = strlen(fmt);    /* 得到可变参数的首地址 */va_start(ptr, fmt);    for(; *fmt; fmt++){        if(*fmt == '%'){            switch(*++fmt){                case 'd':                case 'D':                    sprintf(temp_buf, "%d", va_arg(ptr, int));temp_ptr = temp_buf;                    break;                case 's':                case 'S':                    /* 取出当前变量,并将指针指向下一个可变参数 */temp_ptr = va_arg(ptr, char*);                    break;}arg_len = strlen(temp_ptr);            strcat(buf+index, temp_ptr);index += arg_len;}else{buf[index] = *fmt;index++;}}    /* 结束取参 */va_end(ptr);    /* 输出最终转换结果 */puts(buf);
}int main(){print("My name is %s and my height is %d cm.", "Lance#", 178);    return 0;
}

程序运行结果:

My name is Lance# and my height is 178 cm.

声明:

本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338974.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql error 变量_mysql 变量问题

关于mysql5.5数据库中变量的引用的问题.1.有一个数据库是test_num_base,其中有一个test表.我想通过变量的方式获取test的数据.select * from test_num_base.test;使用变量:set Atest_num_base;但是再次访问使用: select * from A.test;报错:ERROR 1064 (42000): You have an er…

lambda捕获this_非捕获Lambda的实例

lambda捕获this大约一个月前&#xff0c;我在Java 8的lambda表达式框架下总结了Brian Goetz的观点 。 目前&#xff0c;我正在研究有关默认方法的文章&#xff0c;令我惊讶的是&#xff0c;我又回到了Java处理lambda表达式的方式。 这两个功能的交集可能会产生微妙但令人惊讶的…

mysql8 安装_MySQL8.x安装使用

1.下载网址https://dev.mysql.com/downloads/mysql/下载要登录&#xff0c;可以使用Oracle账户登录2.安装MySQL服务下载好的解压到D:developer目录下配置MySQL(windows下是my.ini&#xff0c;Linux下是my.cnf)[mysql]# 设置mysql客户端默认字符集default-character-setutf8[mys…

从原理到方法,一文讲清如何应对C语言内存泄露!

可能不少开发者都遇到过内存泄漏导致的网上问题&#xff0c;具体表现为单板在现网运行数月以后&#xff0c;因为内存耗尽而导致单板复位现象。一方面&#xff0c;内存泄漏问题属于比较浅显的错误&#xff0c;此类问题遗漏到现网&#xff0c;影响不好&#xff1b;另一方面&#…

如何卸载pip 重新安装mysql_pip install cymysql失败的解决办法 | 厘米天空

今天在配置一台后端的时候用pip安装cymysql出现错误&#xff1a;Cannot fetch index base URL http://pypi.python.org/simple/Could not find any downloads that satisfy the requirement cythonNo distributions at all found for cythonStoring complete log in /root/.pip…

openshift_OpenShift上具有NetBeans的Java EE

openshift今天是慕尼黑的NetBeans日 。 我很高兴提出一个关于将Red Hat产品与我著名的IDE集成的会议。 因此&#xff0c;我一直在谈论WildFly &#xff0c; EAP &#xff0c;Git和OpenShift Online&#xff0c;并展示了使用该工具集优化开发工作流程的所有不同方式。 大约有10…

const char * 类型的实参与 char * 类型的形参不兼容_4 种 C++ 强制类型转换,你都清楚吗?...

我们先来回忆以下&#xff0c;C 语言的强制类型转换形式&#xff1a;(type) expr;这种旧式强制类型转换从表现形式上来说不够清晰明了&#xff0c;容易看漏&#xff0c;一旦转换过程出现问题&#xff0c;追踪起来也就更加困难。为了解决以上问题&#xff0c;C不仅兼容了C的强制…

ggplot2中显示坐标轴_R可视化08|ggplot2图层标度图层(scale layer)图例篇

"pythonic生物人"的第106篇分享本文详细介绍ggplot2中图例标度(legends scales)&#xff0c;续前篇R可视化07|ggplot2图层-标度图层(scale layer)-颜色盘篇本文目录4、图例标度(legends scale)图例位置设置修改ggplot2的图例符号ggplot2的图例顺序|方向等花里胡哨设置…

C explicit 关键字详解

explicit关键字的作用explicit关键字在写程序时使用的次数较少,但是仔细观察会发现,在C 标准库中的相关类声明中explicit出现的频率是很高的,那么explicit关键字到底有什么作用呢?接下来我就为大家一一解答。explicit为清晰的;明确的之意.顾名思义,关键字explicit可以阻止隐式…

unsafe java_Java如何以及为什么使用Unsafe?

unsafe java总览 sun.misc.Unsafe至少在Java 1.4&#xff08;2004&#xff09;中就已经存在于Java中。 在Java 9中&#xff0c;不安全性将与许多其他供内部使用的类一起隐藏。 以提高JVM的可维护性。 尽管仍不确定究竟将取代Unsafe到底是什么&#xff0c;但我怀疑将取代Unsafe不…

python决策树算法_决策树算法及python实现

决策树算法是机器学习中的经典算法 1.决策树(decision tree) 决策树是一种树形结构&#xff0c;其中每个内部节点表示一个属性上的测试&#xff0c;每个分支代表一个测试输出&#xff0c;每个叶节点代表一种类别。 假设小明去看电影&#xff0c;影响看电影的外部因素有 时间 电…

mysql 远程服务器返回错误404_远程服务器返回异常: (404) 未找到

代码如下&#xff0c;res 赋不了值&#xff0c;为什么&#xff1f;private HttpWebResponse GetData(string url, string agent null, string prod_id null, string oauth_consumerKey "C16207CBF5444A5BB2499189D1E526D5", string oauth_consumerSecret "8…

长见识:你真的知道C语言里extern quot;Cquot; 的作用吗?

经常在C语言的头文件中看到下面的代码&#xff1a;#ifdef __cplusplus extern "C" { #endif// all of your legacy C code here#ifdef __cplusplus } #endif这通常用于C 和C混合编程的时候&#xff0c;为了防止C 的编译器在编译C文件的时候出现错误&#xff1b;众所周…

python自动批量发邮件脚本_Python实现自动发送邮件功能

简单邮件传输协议(SMTP)是一种协议&#xff0c;用于在邮件服务器之间发送电子邮件和路由电子邮件。Python提供smtplib模块&#xff0c;该模块定义了一个SMTP客户端会话对象&#xff0c;可用于使用SMTP或ESMTP侦听器守护程序向任何互联网机器发送邮件。 SMTP通讯的基本流程可以概…

aem 渲染_AEM中的单元测试(大声思考)

aem 渲染如果要在AEM中进行单元测试&#xff0c;这不是任何建议&#xff0c;而是各种思想的总结和一些可供选择的选项。 一段时间之前&#xff0c;我已经为客户进行了一些研究&#xff0c;这篇文章在很大程度上受到了这项工作的影响&#xff0c;但是很多上下文相关的东西已经被…

C语言中#if,#if defined ,#ifdef,extern的用法描述

1、#if 和#ifdef当asd_eee表达式存在而且&#xff0c;值为ture的时候接续向下执行例如#define TARGET_LITTLE_ENDINA 1 #define TARGET_BIG_ENDINA 0 #ifdef TARGET_LITTLE_ENDINA call little endina function #else call big endina function #endif上面的今天写的代码&…

java 排序性能_Java8排序–性能陷阱

java 排序性能Java 8带来了lambda的所有优点&#xff0c;使我们能够使用声明式样式进行编程。 但这真的免费吗&#xff1f; 我们是否应该担心必须为新的编程功能付出的代价&#xff1f; 这是一个我们可能要担心的例子。 考虑对这个简单类的实例进行排序&#xff1a; private…

delphi报列表索引越界怎么处理_图解Elasticsearch索引机制,此篇带你领悟新世界...

前言随着Elastic的上市&#xff0c;ELK不仅在互联网大公司得到长足的发展&#xff0c;而且在各个中小公司都得到非常广泛的应用&#xff0c;甚至连"婚庆网站"都开始使用Elasticsearch了。随之而来的是 Elasticsearch 相关部署、框架、性能优化的文章早已铺天盖地。因…

为什么C语言函数不能返回数组,却可以返回结构体

C语言函数为什么不能返回数组&#xff1f;在C语言程序开发中&#xff0c;我们不可以编写下面这样的代码&#xff1a;char f(void[8]{ char ret;// ...fill... return ret; }int main(int argc, char ** argv) {char obj_a[10];obj_a f(); }不可以编写这样的代码这其实就是不能…

oracle迁移mysql_从自建Oracle迁移至RDS MySQL

#本示例以名称为dtstest的数据库账号为例介绍授权命令&#xff0c;需要对PDB和CDB同时授权#PDB授权示例&#xff1a;create user dtstest IDENTIFIED BY rdsdt_dtsacct;grant create session to dtstest;grant connect to dtstest;grant resource to dtstest;grant select on a…