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. VC中LINK 2001 和 LINK 2009 的错误的解决

    最近将两个开源C++项目编译成windows版本的时候遇到很多问题,关键是两个项目经过同事的修改之后,一个项目引用了另一个项目,两个项目的头文件中都有一些跨平台的关于数据类型,以及一些通用函数的定义, ...

  2. 用SQL语句断开某个数据库的所有活动连接

    每次一执行完一个数据库脚本,想要做一些别的操作的时候(比如还原数据库),老是有数据库活动连接,烦不胜烦(如下图所示). 下面给出一种删除数据库活动连接的方式.将下面代码段中的“--修改一下”处的数据库 ...

  3. tomcat通过conf-Catalina-localhost目录发布项目详解

    Tomcat发布项目的方式大致有三种,但小菜认为通过在tomcat的conf/Catalina/localhost目录下添加配置文件,来发布项目,是最佳选择. 因为这样对tomcat的入侵性最小,只需 ...

  4. Linux系列笔记 - 用户以及用户组命令

    一.前言 这一系列的随笔笔记,并不是详细的说明的命令的原理,只是简单的记录, 以备后期的查看以及复习 二.直接输入命令问题 有时候,我们在用 useradd groupadd等命令时,直接在终端输入的 ...

  5. Spring基本概念

    spring优点: 1降低组件间耦合度,实现软件各层之间的解耦. 2可以使用容器提供的各种服务.如,事务管理服务,消息服务等等. 当我们使用容器管理事务时,开发人员就不再需要手工控制事务,也不需处理复 ...

  6. JAVA学习Swing章节按钮组件JButton的简单学习

    package com.swing; import java.awt.Container; import java.awt.Dimension; import java.awt.GridLayout; ...

  7. Maven学习总结(四)——Maven核心概念--转载

    一.Maven坐标 1.1.什么是坐标? 在平面几何中坐标(x,y)可以标识平面中唯一的一点. 1.2.Maven坐标主要组成 groupId:组织标识(包名) artifactId:项目名称 ver ...

  8. python中x的平方

    x ** 2 sqdEvens = [x ** 2 for x in range(8) if not x % 2] for i in sqdEvens: print(i) 0 4 16 36 > ...

  9. js高仿QQ消息列表左滑功能

    该组件,主要功能类似于QQ消息列表左滑出现删除.标为已读等按钮的功能:现在的版本用的是纯javaScript编写:后续会跟进 angularJs 开发的类似组件以及jquery的; 下面,就让我们来认 ...

  10. 利用Mongodb的复制集搭建高可用分片,Replica Sets + Sharding的搭建过程

    参考资料 reference:  http://mongodb.blog.51cto.com/1071559/740131  http://docs.mongodb.org/manual/tutori ...