C语言中的很多函数的入参被定义为可变参数,最典型的

int printf (const char * fmt, ...)

要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, VA_END, VA_ARG 宏 ,需要包含<stdarg.h>头文件

利用va族函数对不定参数进行解析的过程所示如下:

 int my_printf(const char * fmt, ...)
{
va_list struAp;
va_start(struAp, fmt); for (; *fmt; ++fmt)
{
if(*fmt != '%')
{
PUTC(*fmt);
continue;
} fmt++; switch (*fmt)
{
case 'd':
{
int i = va_arg(struAp,int);
PUTC(i);
}
break; default:
break;
}
} va_end(struAp);
}

要了解不定参数的处理方式,就要搞清楚va族函数的实现

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

/* 实质就是一个char型的指针 */

typedef char * va_list;

/* 将指针偏移一个v的长度,指向后面的地址 */

#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )

/* 根据参数类型,取出后面的数据,强制类型转换 */

#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

/* 将指针置为NULL */

#define va_end(ap) ( ap = (va_list)0 )

通过解析fmt字符串,得到后面的参数类型和个数,根据参数类型再加上偏移量就可以找到栈中的不定参数了

函数调用和传参的过程所示如下:

将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。

例如

void func(int param1, double param2,int param3){ }

int main()
{
func(, 1.2, );
printf("Over\n"); //设指令地址为0x1234
return ;
}

执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:

 
 这样,通过param1的地址就可以计算出param2与param3的地址:
 
使用不定参函数的注意事项
 
  • 在C语言中,调用一个可变参数函数时,调用者会对每个参数执行“默认实际参数提升(default argument promotions)”

——float类型的实际参数将提升到double
  ——char类型的实际参数将提升到int
  ——short类型的实际参数将提升到int

  • 在没有函数原型的情况下,char与short类型都将被转换为int类型,float类型将被转换为double类型。

——《C语言程序设计》第2版  2.7
类型转换 p36

  • 这样写肯定是不对的:

   c = va_arg(ap,char);

  因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。上面的式子应该写成:
  c = va_arg(ap,int);

               
——《C陷阱与缺陷》p164

C语言函数可变长度参数剖析的更多相关文章

  1. C语言函数可变参数列表

    C语言允许使用可变参数列表,我们常用的printf函数即为可变参数函数,C标准库提供了stdarg.h为我们提供了这方面支持:该头文件提供了一些类型和宏来支持可变参数列表,包括类型va_list,宏v ...

  2. C语言函数不定参数实现方式

    函数如何实现不定参数: 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. (1)va_ ...

  3. GO 函数的参数

    一.函数 函数的参数 1.1 参数的使用 形式参数:定义函数时,用于接收外部传入的数据,叫做形式参数,简称形参. 实际参数:调用函数时,传给形参的实际的数据,叫做实际参数,简称实参. 函数调用: ​ ...

  4. C 语言函数参数只能传指针,不能传数组

    今天被要求编写一个C/C++冒泡算法的程序,心想这还不是手到擒来的事儿,虽然最近都是用Javascript程序,很少写C/C++程序,但是好歹也用过那么多年的C语言: 首先想的是怎么让自己的代码看上去 ...

  5. Swift 1.1语言函数参数的特殊情况本地参数名外部参数名

    Swift 1.1语言函数参数的特殊情况本地参数名外部参数名 7.4  函数参数的特殊情况 声明定义有参函数时,为函数的每一个参数都定义了参数名称.根据参数名定义的形式不同,函数参数包括本地参数和外部 ...

  6. [转]深度探索C语言函数可变长参数

    转自:http://www.cnblogs.com/chinazhangjie/archive/2012/08/18/2645475.html 一.基础部分 1.1 什么是可变长参数 可变长参数:顾名 ...

  7. C语言函数参数压栈顺序为何是从右到左?(从左向右的话,碰到printf的会陷入死循环)

    上学期学习了汇编语言,并在操作系统实验中使用了汇编+C语言混合编程,中间也了解了一些C语言与汇编语言的对应关系. 由于汇编语言是底层的编程语言,各种函数参数都要直接控制栈进行存取,在混合编程中,要用汇 ...

  8. C语言中函数可变参数解析

    大多数时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定.典型的例子有 大家熟悉的函数printf().scanf ...

  9. 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

    . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshul ...

随机推荐

  1. OpenGL(二)——函数库简述

    简述 1. OpenGL用到的库分为OpenGL核心库.OpenGL实用库.OpenGL辅助库和OpenGL工具库(不是必须) 2. OpenGL核心库 核心库包含有115个函数,函数名的前缀为gl ...

  2. ftp客户端命令使用简记

    OS:windows8.1评估版 程序和功能 tftp客户端勾选上 Win+R:运行,键入cmd,键入ftp -help 如下图: 使用ftp客户端可以做的事:将文件传送到运行FTP服务器服务(经常称 ...

  3. Dynamic CRM 2013学习笔记(七)追踪、监控及性能优化

    本文将介绍CRM的三个内容追踪.监控及性能优化.追踪是CRM里一个很有用的功能,它能为我们的CRM调试或解决错误.警告提供有价值的信息:我们可以用window的性能监控工具来了解CRM的性能状况:最后 ...

  4. [转]解决WebClient或HttpWebRequest首次连接缓慢问题

    http://blog.csdn.net/rrrfff/article/details/6170653?reload 设置代理为空: <?xml version="1.0"? ...

  5. UML类图基本元素符号

     UML类图基本元素符号 元素名称 符号图例 含义 Class 包含类的名称.属性和方法定义. 可见性修饰符: + public -  private # protected 无修饰符为 intern ...

  6. C语言 栈 链式结构 实现

    一个C语言链式结构实现的栈 mStack (GCC编译). /** * @brief C语言实现的链式结构类型的栈 * @author wid * @date 2013-10-30 * * @note ...

  7. node-webkit教程(9)native api 之Tray(托盘)

    node-webkit教程(9)native api 之Tray(托盘) 文/玄魂 目录 node-webkit教程(9)native api 之Tray(托盘) 前言 9.1  Tray简介 9.2 ...

  8. 初学者利用git 上传代码到Coding的简单操作步骤

    1.首先登陆coding网站注册账号https://coding.net/ (注册完后需登陆邮箱激活邮件) 2.登陆刚注册的coding账号 ,添加项目 添加项目—〉输入项目名称—〉输入对项目的简单描 ...

  9. [ucgui] 对话框7——按钮触发与模式窗口

    >_<" 模式窗口,只有结束该窗口时才能聚焦到其他的窗口上~

  10. 升级webapi依赖的Newtonsoft.json的版本

    随着微软日渐重视开源社区的贡献,微软在自己的产品中往往也会集成开源的第三方库. 比如System.Net.Http.Foramatting.dll 就依赖于Newtonsoft.json v4.5. ...