可变参数的函数的原理

原文地址:

可变参数。
1:必须有一个提前参数,(即:...之前必须要有一个参数),用以计算出后面的第一个未知参数的地址. 知道了第一个未知参数的地址之后, 就可以根据fmt格式化串,可以依次计算出剩余的参数地址.
sprintf()的原型:sprintf(char* buffer, const char* fmt, ... ) ,其中,fmt就是提前参数
2:每一个可变阐述函数,其编写者与使用者 都要有一个参数的使用约定。不然,会乱套。

3:可变函数实现的技术基础
1:所有参数,在汇编级别,其大小都是4个字节的整数倍。char,short类型也被扩展成4byte
把任意长度的类型扩展成4byte的整数倍的技术就是 
_INTSIZEOF(n) ( (sizeof(n)+sizeof(int)-1) & ~(sizeof(int)-1))
简化就是,((sizeof(n)+4-1) & ~(4-1))
_INTSIZEOF(char*) = 4
_INTSIZEOF(2byte) = 4
_INTSIZEOF(double)= 8

2:有了上述的知识,下面就是,从可变参数中一个一个去参数了
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
               // :取得已知参数的地址,
               // ap 就是第一个未知参数地址,v就是上述的提前参数fmt
               // eg :fun( int n,...) v就是n的地址。

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

va_arg()替换后,ap指向t类型后的下一个类型的地址。va_arg本身被替换成了t类型对应参数的值. t类型一般是从fmt格式化串中根据%d,%c等转换的.
比如1: int a = va_art(ap, int);

比如2: fun(int n,...)
{
...
char* pChar = va_arg(ap, char*)
int tmp = va_arg(ap, int)
double dd = va_arg(ap, double)
}
那么,...必然是 char*,int,double的参数顺序!!如果不是这样的顺序,程序将不定期崩溃!!这就

是为什么c不是类型安全的,boost::Format好像提供类型安全的格式转换,可以替代sprintf的使用。
对于sprintf()而言,va_arg(av,v)v的值,使用 char* fmt 中提取的。
 
附:

3)//示例代码2:扩展——自己实现简单的可变参数的函数。(没有用到va_list等宏)
下面是一个简单的printf函数的实现,参考了<The C Programming Language>中的例子
#include "stdio.h"
#include "stdlib.h"
void myprintf(char* fmt, ...)        //一个简单的类似于printf的实现,//参数必须都是int 类型

    char* pArg=NULL;               //等价于原来的va_list 
    char c;
    
    pArg = (char*) &fmt;          //注意不要写成p = fmt !!因为这里要对//参数取址,而不是取值
    pArg += sizeof(fmt);         //等价于原来的va_start          
 
    do
    {
        c =*fmt;
        if (c != '%')
        {
            putchar(c);            //照原样输出字符
        }
        else
        {
           //按格式字符输出数据
           switch(*++fmt) 
           {
            case'd':
                printf("%d",*((int*)pArg));           
                break;
            case'x':
                printf("%#x",*((int*)pArg));
                break;
            default:
                break;
            } 
            pArg += sizeof(int);               //等价于原来的va_arg
        }
        ++fmt;
    }while (*fmt != '/0'); 
    pArg = NULL;                               //等价于va_end
    return; 
}
int main(int argc, char* argv[])
{
    int i = 1234;
    int j = 5678;
    
    myprintf("the first test:i=%d/n",i,j); 
    myprintf("the secend test:i=%d; %x;j=%d;/n",i,0xabcd,j); 
    system("pause");
    return 0;
}

时间: 2024-09-17 03:50:01

可变参数的函数的原理的相关文章

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/c++支持可变参数的函数

一.为什么要使用可变参数的函数? 一般我们编程的时候,函数中形式参数的数目通常是确定的,在调用时要依次给出与形式参数对应的所有实际参数.但在某些情况下希望函数的参数个数可以根据需要确定,因此c语言引入可变参数函数.这也是c功能强大的一个方面,其它某些语言,比如fortran就没有这个功能. 典型的可变参数函数的例子有大家熟悉的printf().scanf()等. 二.c/c++如何实现可变参数的函数? 为了支持可变参数函数,C语言引入新的调用协议, 即C语言调用约定 __cdecl . 采用C/

C语言中实现可变参数函数

通过stdarg.h头文件为函数提供了定义可变参数列表的能力.声明一个可变参数的函数类似: void f1(int n,...); 其中n表示参数列表个数,而用省略号来表示未知参数列表.stdarg.h中提供了一个va_list类型,用于存放参数.一个大概的使用过程类似: void f1(int n,...) {    va_list ap;    va_start(ap,n);   //初始化参数列表    double first=va_arg(ap,double);  //取第一个参数  

Swift中的可变参数函数介绍_Swift

可变参数函数指的是可以接受任意多个参数的函数,我们最熟悉的可能就是 NSString 的 -stringWithFormat:方法了.在 Objective-C 中,我们使用这个方法生成字符串的写法是这样的: 复制代码 代码如下: NSString *name = @"Tom"; NSDate *date = [NSDate date]; NSString *string = [NSString stringWithFormat:                 @"Hell

C/C++语言中可变参数的用法

我们在C语言编程中会遇到一些参数个数可变的函数,例如printf() 这个函数,它的定义是这样的:  int printf( const char* format, ...);  它除了有一个参数format固定以外,后面跟的参数的个数和类型是  可变的,例如我们可以有以下不同的调用方法:  printf("%d",i);  printf("%s",s);  printf("the number is %d ,string is:%s", i,

关于C/C++中可变参数的详细介绍(va_list,va_start,va_arg,va_end)_C 语言

由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. 如printf()函数,其原型为:int   printf(   const   char*   format,   ...); 它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的,例如我们可以有以下不同的调用方法:   printf( "%d ",i);   printf( "%s "

C++可变参数的实现方法_C 语言

可变参数的实现要解决三个问题: 1.如何调用带有可变参数的函数2.如何编译有可变参数的程序3.在带有可变参数的函数体中如何持有可变参数第一个问题, 调用时在可以传入可变参数的地方传入可变参数即可,当然,还有一些需要注意的地方,后面会提到. 第二个问题,编译器需要在编译时采用一种宽松的检查方案,,这会带来一些问题, 比如对编程查错不利. 第三个是我在这里要关心的问题,先以C语言为例分析其实现原理. printf和scanf是C语言标准库中最常见的可变参数函数, printf的签名是 复制代码 代码

《企业级ios应用开发实战》一3.6 可变参数

3.6 可变参数 我们知道,C和C++语言支持可变参数的函数,例如我们常用的NSLog和printf函数.Objective-C作为C语言的超集,当然毫无例外地也支持可变参数.迄今为止,我们至少用过了一种使用可变参数的方法,即NSString的stringWithFormat:方法. C语言通过stdarg.h库支持可变参数,Objective-C 也不例外.在C语言中,如果你要使用可变参数,必须包含头文件stdarg.h,但在Cocoa中却不必,因为苹果已经在 NSObjC Runtime.h

PostgreSQL 如何计算可变参数中有没有重复参数

PostgreSQL 如何计算可变参数中有没有重复参数.有一个计算排列组合的算法需求,需要知道参数中是否有重复值.目的是过滤掉重复值.实现方法很简单,需要创建支持可变参数的函数,如下: postgres=# CREATE or replace FUNCTION has_dupli_val(VARIADIC arr int[]) RETURNS boolean AS $$ select count(distinct val)<>count(*) dist_val from unnest($1)