基础:va_start和va_end使用详解

本文主要介绍va_start和va_end的使用及原理。

  介绍va_start和va_end这两个宏之前,先看一下C中传递函数的参数时的用法和原理:

1、在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

void foo(...);
void foo(parm_list,...);

这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。

2、函数参数的传递原理

  函数参数是以数据结构栈的形式存取,从右至左入栈

  举个例子如下:void func(int x, float y, char z);

  那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。

  下面是 <stdarg.h> 里面重要的几个宏定义如下:

先假设有函数:void demo( char para, ... ) ;
typedef char* va_list;
void va_start (va_list ap, para); /* ANSI version */
type va_arg (va_list ap, type);
void va_end (va_list ap);

va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。

1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量定义为ap);

2> 应该对ap 进行初始化,让它指向"可变参数表"里面的第一个参数,通过调用va_start来实现的,第一个参数是 ap 本身,第二个参数是在"变参表"前面紧挨着的变量即para,也就是“...”之前的那个参数;可变参数表就是“...”,简称变参表

3> 若想获取参数调用va_arg,它的第一个参数是ap,第二个参数是要获取参数的类型,然后返回此类型的值,这两个类型必须一致,并把 ap 的位置指向"变参表"的下一个变量位置;

4> 获取所有的参数之后,有必要将这个 ap 指针关掉,以免发生危险,调用va_end方法将输入参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。

例如 int max(int n, ...); 其函数内部应该如此实现:

#include <iostream.h>
void fun(int a, ...)
{
  int *temp = &a;
  temp++;
  for (int i = 0; i < a; ++i)
  {
    cout << *temp << endl;
    temp++;
  }
}
int main()
{
  int a = 1;
  int b = 2;
  int c = 3;
  int d = 4;
  fun(4, a, b, c, d);
  return 0;
}
Output::
1
2
3
4

3、获取省略号指定的参数

  在函数体中声明一个va_list,然后用va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束。像这段代码:

void TestFun(char* pszDest, int DestLen, const char* pszFormat, ...)
{
va_list ap;
va_start(ap, pszFormat); //pszFormat一定要是“...”之前的那个参数
_vsnprintf(pszDest, DestLen, pszFormat, ap);
va_end(ap);
}

4、演示如何使用参数个数可变的函数,采用ANSI标准形式

函数原型声明,至少需要一个确定的参数,注意括号内的省略号

#include 〈stdio.h〉
#include 〈string.h〉
#include 〈stdarg.h〉

int demo( char *, ... );
void main( void )
{
 demo("DEMO", "This", "is", "a", "demo!", "");
} 

/*ANSI标准形式的声明方式,括号内的省略号表示可选参数*/
int demo( char* msg, ... )
{
 /*定义保存函数参数的结构*/
 va_list ap;
 int num = 0;
 char *para;
 /*ap指向传入的第一个可选参数,msg是最后一个确定的参数*/
 va_start( ap, msg );
 while (1)
 {
 para = va_arg(ap, char *); // 注:变量para的类型要与va_arg里的第二参数类型要一致
 if (strcmp(para, "") == 0)
 break;
 printf("Parameter #%d is: %s\n", num, para);
 num++;
}
va_end(ap);
/*将ap置为NULL*/
return 0;
}

以上是对va_start和va_end的介绍。

时间: 2024-10-29 01:41:50

基础:va_start和va_end使用详解的相关文章

C#基础语法:方法参数详解

这篇文章主要介绍了C#基础语法:方法参数详解,本文讲解了值参数.引用参数.输出参数.参数数组等参数类型,并分别给出代码实例,需要的朋友可以参考下     ●值参数 :一个值参数相当于一个局部变量,当使用值参数的时候,将会分配一个新的存储位置,将实参拷贝到该位置,并将该拷贝值传递给该方法.因此,值参数只能将值带进方法,但是不能带出方法,而不会影响实参的值. ●引用参数:当使用引用参数的时候,将不会分配一个新的存储位置,In other words,引用参数能将值带进方法,也能带出方法,因而会影响实

Python基础之函数用法实例详解_python

本文以实例形式较为详细的讲述了Python函数的用法,对于初学Python的朋友有不错的借鉴价值.分享给大家供大家参考之用.具体分析如下: 通常来说,Python的函数是由一个新的语句编写,即def,def是可执行的语句--函数并不存在,直到Python运行了def后才存在. 函数是通过赋值传递的,参数通过赋值传递给函数 def语句将创建一个函数对象并将其赋值给一个变量名,def语句的一般格式如下: def <name>(arg1,arg2,arg3,--,argN): <stateme

iOS对象指针和基础数据类型的强转详解_IOS

本文主要介绍了iOS中对象指针和基础数据类型如何进行强转,下面话不多说,直接来看示例详解. 一.对象指针的强转: UIView *view = [UIView new];//new一个UIView类的对象 UILabel *label = (UILabel *)view;//强转成UILabel指针 label.text = @"123";//给label的text属性赋值(调用label的setText方法) 上述代码会产生崩溃,崩溃信息如下: -[UIView setText:]:

linux基础命令(31) etcgroup文件详解

Linux /etc/group文件与/etc/passwd和/etc/shadow文件都是有关于系统管理员对用户和用户 组管理时相关的文件.linux /etc/group文件是有关于系统管理员对用户和用户组管理的文件,linux用户组的所有信息都存放在 /etc/group文件中.具有某种共同特征的用户集合起来就是用户组(Group).用户组(Group)配置文件主要有 /etc/group 和/etc/gshadow,其中/etc/gshadow是/etc/group的加密信息文件. 将用

jquery基础教程之数组使用详解

 jQuery的数组处理.便捷.功能齐全.一步到位的封装了很多原生JavaScript数组不能企及的功能.下面是jquery数组的使用详解,需要的朋友可以参考下 1. $.each(array, [callback]) 遍历[常用]   解释: 不同于例遍jQuery对象的$().each()方法,此方法可用于例遍任何对象.回调函数拥有两个参数:第一个为对象的成员或数组的索引, 第二个为对应变量或内容. 如果需要退出each循环可使回调函数返回false, 其它返回值将被忽略.   each遍历

Android基础知识之broadcast广播详解_Android

Android中的广播用的太多了,今天稍微总结一下. 按注册方式分为两种: 1.静态注册广播: 静态注册广播就是在androidManifest.xml文件中注册广播,假设我们要实现这样一个效果,在一个activity上点击按钮,发送一条广播,这条广播弹出一个toast,显示"静态"二字. 先看看广播接受者: public class MyBroadcast extends BroadcastReceiver { @Override public void onReceive(Cont

Photoshop入门基础教程:色相/饱和度命令详解

"色相/饱和度"命令是较为常用的色彩调整命令.可以调整整个图像的色相.饱和度和明度,在本节的学习中,就像朋友们讲述"色相/饱和度"命令的应用原理. 1. 了解色彩的基本属性 要掌握"色相/饱和度"命令,首先要理解色彩的基本属性,即:色相.饱和度.明度. "色相"是色彩的首要外貌特征,除黑白灰以外的颜色都有色相的属性,是区别各种不同色彩的最准确的标准.例如以下图片中图像的背景色为不同的色相:黄色.红色.绿色. [1] [2] [

linux基础命令(25) linux文件属性详解

Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模式.链接数量.所归属的用户和用户组.最近访问或 修改的时间等内容.具体情况如下: 命令: ls -lih 输出: [root@localhost test]# ls -lih 总计 316K 2095120 lrwxrwxrwx 1 root root   11 11-22 06:58 linklog.log -> log2012.log 2095112 -rw-r--r-- 1 root root 296K 11-13 06:

python基础教程之字典操作详解_python

字典dictionary 1.键值对的集合(map) 2.字典是以大括号"{}"包围的数据集合 3.字典是无序的,在字典中通过键来访问成员. 可变的,可嵌套,可以原处修改扩展等,不产生新的字典 4.字典的键,可以是字符串(大小写敏感),数字常量或元组(不可变类型),同一个字典的键可以混用类型.字典的键必须是可哈希的 元组作为键的条件是,元组内的值都是不可变类型 复制代码 代码如下: a = (1,2)  #可以作为键b = (1,2,[3,4])  #不可以 5.字典的值可以是任意类型