解析可变参数函数的实现原理(printf,scanf)

From: http://hi.baidu.com/huifeng00/blog/item/085e8bd198f46ed3a8ec9a0b.html

 

学习C的语言的时候,肯定接触到标准输出和标准输入函数。

这个函数给人的感觉很强大,因为它很另类,就是这个函数的参数是可变的。

下面是一个自己编写的可变参数的函数,它的功能是求和。如下

#include <stdio.h>
#include <stdarg.h>
int sum(int data,...)
{
    int i=data,s=0;
va_list vl;
va_start(vl,data);
while(i!=-1)
{
   s+=i;
   i=va_arg(vl,int);
}
va_end(vl);
return s;
}
int main()
{
    int s=sum(1,2,3,4,5,-1);
printf("sum = %d\n",s);
return 0;
}

程序如上,注意以下几点就可以编写可变参数的函数了。

1.声明

int sum(int data,...)

它的末尾是以...结束的,表示是可变参数函数。

2.正确使用

va_list

va_start,va_arg,va_end

如上,就可以编写可变参数函数了。

不过大多数初级学者可能对

va_list

va_start,va_arg,va_end

望而却步,认为他们很神秘。

其实他们也是很基础的C知识,只是被包装了。

C中包装的方法,有 typedef,#define

例如你把int包装成ID

typedef int ID;

其实

va_list

va_start,va_arg,va_end

他们也是包装得到的。

va_list 就是一个指针类型。

va_start,va_arg,va_end,就是3个宏。

下面给出它们在C中的源码


如上你可以在头文件stdarg.h中查到,本文是针对VC6.0来说的,高版本的编译器,在vadefs.h,它的定义是

类似的。

不过本文关键是说实现的原理:

首先在你调用一个可变参数函数时,例如

上面的

int s=sum(1,2,3,4,5,-1);

这个函数,编译器会在内存中分配空间存储这些参数。

根据编译器中从右至左的顺序把参数压栈。

这里的栈就是常说的存储局部变量和函数参数的内存空间。

这块内存空间是连续的。

因此我们只要能找到这块内存空间的首地址就可以了。然后每读一个参数,就加上这个参数在内存空间中占

的大小,就是下一个参数的内存地址,就这样依次就可以得到每一个参数。

下面说下每个标识的含义

1,va_list 就是一个char* 指针,用来记录这个参数列表在内存中地址。


2,va_start()

例如:va_start(vl,data);这个宏是得到参数列表中第2个参数内存地址。

至于为什么不是第一个,这和写编译器的程序员有关,因为它的宏定义,就是说明这个vl,注(vl就是va_list)

指向的就是第2个参数。



如上,v就是第一个参数,ap就是va_list的变量,例如上面的

va_start(vl,data);

很明显它先得到第一个参数内存地址,然后又加上这个参数的内存大小,就是下个参数的内存地址。

注:因为第一个参数,参数列表中已经给出,所以它得到的是第2个参数的内存地址。

3,va_arg

这个宏的意思,就是取得当前vl所指的参数,并且vl加上这个参数大小,指向下一个参数。

它的定义,明显说明了这个问题。


它先加上参数类型t的大小,然后,在减去,参数类型t的大小,然后作强制类型转换(t*),所以t一定要是类

型,不是变量。然后取*得到改地址指向的内存中的数据。

例如实例中的

i=va_arg(vl,int);

第一次调用的时候,它得到就是参数列表中第二个参数的值。

下面依次调用就可以得到第三个,第四个参数的值。。。

4,va_end
这个宏,就比较简单了。

就是把指针值归0.让它指向NULL。

也就是一个指针不用了,就会把它赋值为NULL.


如上明先可以看到

这个宏就是一个赋值语句。

ap=(char*)0;//va_list 就是cha*的别名。

其实只要把4个标识

va_list

va_start,va_arg,va_end

的意思记住了,就可以编写可变参数的函数。

使用的主要注意

va_start(ap,v)

v是第一个参数。

va_arg(ap,t)

t是你要取得的参数类型。

va_end(ap)

就是把指针ap赋值为0,使他不指向内存的变量。

ap

就是

va_list的一个变量。也就是一个

char *类型的变量。

到此,我想你应该对可变参数函数的实现原理有了一定的了解,起码应该有了形象的了解吧。

coder:huifeng00

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

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

相关文章

花生葫芦球 健身新运动

国民健康天后张淳淳老师率先再将风靡欧美的“花生葫芦球(FITNESS BALL)”&#xff0c;推广给日、港、台的朋友&#xff0c;同时结合国内外体适能教练与专家&#xff0c;研发出一套减压、塑身运动课程&#xff0c;引领全民健康塑身运动。 花生葫芦球 健身新运动美大腿后健肌群伸…

JS之数组元素排序方法sort

作用&#xff1a;sort() 方法用于对数组的元素进行排序 语法&#xff1a;arrayObject.sort(sortby) 参数&#xff1a;可选。规定排序顺序。必须是函数 返回值&#xff1a;对数组的引用。请注意&#xff0c;数组在原数组上进行排序&#xff0c;不生成副本 注意1&#xff1a;…

js中自己实现bind函数的方式

前言 最近由于工作比较忙&#xff0c;好久都没时间静下心来研究一些东西了。今天在研究 call 和 apply 的区别的时候&#xff0c;看到 github 上面的一篇文章&#xff0c;看完以后&#xff0c;感觉启发很大。 文章链接为 https://github.com/lin-xin/blog/issues/7 &#xff…

我的C语言可变参数的实现

实现环境&#xff1a;Fedora12 gcc 任务&#xff1a;用C语言实现一个参数可变的函数&#xff0c;以方便输出。 源代码如下&#xff1a; #include <stdio.h>#include <stdarg.h>#include <string.h>int sum(int data, ...){int i data, s 0;va_list vl;…

Leetcode刷题(1)两数之和

最好的种树是十年前,其次是现在。歌谣 每天一个前端小知识 提醒你改好好学习了 知乎博主 csdn博主 b站博主 放弃很容易但是坚持一定很酷 我是歌谣 喜欢就一键三连咯 你得点赞是对歌谣最大的鼓励 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中…

使用WEB方式更改域用户帐户密码

使用WEB方式更改域用户帐户密码 <?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />1、这个只是域帐户密码的一种更改方式&#xff0c;正规来说&#xff0c;域用户帐户的密码更改方式可以有6种。今天介绍给大家的只是其中一种&…

一个路径下挂载(匹配)多个子组件

效果图如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>Document</title><script type"text/javascript" src"./lib/vue-2.4.0.js"></script><scrip…

JS之字符串截取函数substr

作用&#xff1a;substr() 方法可在字符串中抽取从 start 下标开始的指定数目的字符 语法&#xff1a;stringObject.substr(start,length) 参数1&#xff1a;必需。要抽取的子串的起始下标。必须是数值。如果是负数&#xff0c;那么该参数声明从字符串的尾部开始算起的位置。…

面向对象中的修饰关键词

final:用来修饰类和方法&#xff0c;修饰类的时候表示这个类是终极类&#xff0c;不能被其他类继承&#xff0c;修饰方法的时候&#xff0c;表示这个方法是终极方法&#xff0c;不能被子类重写。 static:用来修饰属性和方法&#xff0c;修饰属性的时候表示这个属性是静态属性&a…

GDB命令大全

GDB的使用   当程序出错并产生core 时   快速定位出错函数的办法   gdb 程序名 core文件名(一般是core,也可能是core.xxxx)   调试程序使用的键   r run 运行.程序还没有运行前使用   c cuntinue 继续运行。运行中断后继续运行   q 退出   kill 终止调…

Leetcode刷题(2)回文数

最好的种树是十年前,其次是现在。歌谣 每天一个前端小知识 提醒你改好好学习了 知乎博主 csdn博主 b站博主 放弃很容易但是坚持一定很酷 我是歌谣 喜欢就一键三连咯 你得点赞是对歌谣最大的鼓励 给你一个整数 x &#xff0c;如果 x 是一个回文整数&#xff0c;返回 true &…

ZT Web Control 开发系列(一) 页面的生命周期

http://www.cnblogs.com/joeliu/category/143125.htmlPage是WebForm编程基本元素&#xff0c;它从TemplateControl派生&#xff0c;而TemplateControl又从Control派生&#xff0c;所以Page实际就是一个Control。同时Page也实现了IHttpHandler接口&#xff0c;所以它可以接受Htt…

计算属性computed的使用

效果图 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>Document</title><script type"text/javascript" src"./lib/vue-2.4.0.js"></script></head>…

JS之字符串截取方法substring

作用&#xff1a;substring() 方法用于提取字符串中介于两个指定下标之间的字符 语法&#xff1a;stringObject.substring(start,stop) 参数1&#xff1a;必需。一个非负的整数&#xff0c;规定要提取的子串的第一个字符在 stringObject 中的位置 参数2&#xff1a;可选。一…

gdb命令手册

GDB 的命令很多&#xff0c;本文不会全部介绍&#xff0c;仅会介绍一些最常用的。在介绍之前&#xff0c;先介绍GDB中的一个非常有用的功能&#xff1a;补齐功能。它就如同Linux下SHELL中的命令补齐一样。当你输入一个命令的前几个字符&#xff0c;然后输入TAB键&#xff0c;如…

HTML5增加的几个新的标签

HTML5又2008年诞生&#xff0c;HTML5大致可以等同于htmlcss3javascriptapi.... so --->支持css3强大的选择器和动画以及javascript的新的函数 先来记录一下吧&#xff01; 1。 <canvas>画布标签 HTML5的新标签 举例&#xff1a; 1 <html>2 <head>3 …

在 Linux 中使用动态磁盘

是否遇到过这样的问题&#xff0c;划分了<?xml:namespace prefix st1 ns "urn:schemas-microsoft-com:office:smarttags" />10G的一个分区&#xff0c;挂接到/home 下&#xff0c;可是随着时间的流逝&#xff0c;10G的空间开始不够用了&#xff0c;需要把它…

vue项目结构(未抽离成.vue文件前的结构)

最终效果 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>vue项目结构</title><script type"text/javascript" src"./lib/vue-2.4.0.js"></script><scri…