C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数

在定义可变参数的函数之前,先来理解一下函数参数的传递原理:

1、函数参数是以栈这种数据结构来存取的,在函数参数列表中,从右至左依次入栈。

2、参数的内存存放格式:参数的内存地址存放在内存的堆栈段中,在执行函数的时候,从最后一个(最右边)参数开始入栈。因此栈底高地址,栈顶低地址,举个例子说明一下:

void test(int a, float b, char c);

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

实现一个可变参数的函数,需要用到以下几个宏:

typedef char* va_list;	// 用于声明一个指向参数列表的字符型指针变量
void va_start(va_list ap, prev_param);	// 第一个参数为指向可变参数字符指针变量,第二个参数是可变参数的第一个参数,通常用于指定可变参数列表中参数的个数
void va_arg(va_list ap, type);	// 第一个参数为指向可变参数字符指针变量,第二个参数是可变参数的数据类型
void va_end(va_list ap);// 将存放可变参数字符串的变量清空(赋值为NULL)

3、示例:求N个数的和

int sum(int count, ...)
{
    int sum = 0;
    int i;
    va_list ap;
    va_start(ap, count);
    for (i = 0; i < count; ++i)
    {
        sum += va_arg(ap, int);
    }
    va_end(ap);
    return sum;
}
int main(int argc, const char * argv[])
{
    int ret = sum(5, 1, 2, 3, 4, 5);
    printf("sum: %d\n",ret);
}

4、示例:标准库可变参数的相关函数使用

void test(int count,...)
{
    va_list ap;
    va_start(ap, count);
    vprintf("%d,%d,%d\n", ap);                  // 格式化输出可变参数的值

    char buff[1024];
    vsprintf(buff, "a=%d,b=%d,c=%d\n", ap);     // 将可变参数列表的值格式化输出到缓冲区中
    printf("%s\n",buff);

    vfprintf(stdout, "a=%d,b=%d,c=%d\n", ap);   // 将可变参数列表的值打印到标准输出中

    // 可变参数传入的是int类型的地址
    vsscanf("10,30,40", "%d,%d,%d", ap);        // 依次将格式化字符串的值,输入到可变参数变量中

    vsnprintf(buff, 30, "a=%d,b=%d,c=%d", ap);  // 将可变参数列表的值,格式化输出指定长度(30个字符)的字符串到缓冲区中
    printf("vsnprintf=%s\n",buff);

    va_end(ap);
}
时间: 2024-10-14 00:57:33

C语言利用va_list、va_start、va_end、va_arg宏定义可变参数的函数的相关文章

C语言利用va_list、va_start、va_end、va_arg宏定义可变參数的函数

在定义可变參数的函数之前,先来理解一下函数參数的传递原理: 1.函数參数是以栈这样的数据结构来存取的,在函数參数列表中,从右至左依次入栈. 2.參数的内存存放格式:參数的内存地址存放在内存的堆栈段中,在运行函数的时候,从最后一个(最右边)參数開始入栈.因此栈底高地址,栈顶低地址,举个样例说明一下: void test(int a, float b, char c); 那么,在调用test函数的时候,实參char c先进栈,然后是float b,最后才是int a,因此在内存中变量的存放次序是c-

关于C++函数接受数量不定的实参及cstdarg头文件中的va_list,va_start,va_end,va_arg使用范例。

编译环境:Vs2012: //Handling a variable number of arguments ///////////////////////////////////////////////////////////////////////////// #include <iostream>#include<stdlib.h>#include<cstdarg> using std::cout;using std::endl; int sum (int cou

C语言中可变参数的函数(三个点,“...”)

C语言中可变参数的函数(三个点,"...") 本文主要介绍va_start和va_end的使用及原理. 在以前的一篇帖子Format MessageBox 详解中曾使用到va_start和va_end这两个宏,但对它们也只是泛泛的了解. 介绍这两个宏之前先看一下C中传递函数的参数时的用法和原理: 1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表 void foo(...); void foo(parm_list,...); 这种方式和我们以前认识的不大

可变参数的函数与宏

1.可变参数的函数 <stdarg.h>头文件中定义了一些宏,用于可变参数的函数,如下 va_list:这种类型声明局部状态变量,假设变量名为va(用于下面的描述),用于遍历函数. va_start:这个宏初始化状态变量va,要先调用之后才能调用va_arg与va_end. va_arg:这个宏返回参数表中下一个参数的值,将内部指针(在va中)移到下一个参数,下一个参数的类型要用type指定,使va_arg能够计算其在堆栈中的长度,调用va_start之后第一次调用va_arg返回第一个可变参

C宏定义不定参数

1.# 假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预 处理运算符,它可以把语言符号转化程字符串.例如,如 果x是一个宏参量,那么#x可以把参数名转化成相应的字符串.该过程称为字符串化(stringizing). #incldue <stdio.h> #define PSQR(x) printf("the square of" #x "is %d./n",(x)*(x)) int main(void){   

iOS va_list,va_start,va_end

- (id)initWithTitle:(NSString *)title message:(NSString *)message completionBlock:(void (^)(NSUInteger buttonIndex, EMAlertView *alertView))block cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... { /

宏与可变参数

对于打印函数printf我们太熟悉不过,但真是这样吗?看看其原型:     int printf( const char *format [, argument]... ); 等等,末尾的…是什么意思?省略号?是想留给读者无限的遐想空间?你大可这样认为,因为这是不固定参数,简称不定参数,换句话说,有多少参数都行,只要你敢想. 我们想看看可变参数如何使用,并且怎样实现可变参数.先看下面的例子: #include<stdio.h> #include<malloc.h> #include

6、C_宏定义与预处理、函数与函数库

C语言预处理理论 由源码到可执行程序的过程 源码.c->(编译)->elf可执行程序 源码.c->(编译)->目标文件.o->(链接)->elf可执行程序 源码.c->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序 源码.c->(预处理)->预处理过的.i源文件->(编译)->汇编文件.S->(汇编)->目标文件.o->(链接)->elf可执行程序

宏定义与内联函数

1.宏定义的规则和使用解析(1)宏定义的解析规则就是:在预处理阶段由预处理器进行替换,这个替换是原封不动的替换.(2)宏定义替换会递归进行,直到替换出来的值本身不再是一个宏为止.(3)一个正确的宏定义式子本身分为3部分:第一部分是#dedine ,第二部分是宏名 ,剩下的所有为第三部分.(4)宏可以带参数,称为带参宏.带参宏的使用和带参函数非常像,但是使用上有一些差异.在定义带参宏时,每一个参数在宏体中引用时都必须加括号,最后整体再加括号,括号缺一不可. 宏定义示例1:MAX宏,求2个数中较大的