加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

C可变参数的函数

发布时间:2020-12-16 09:09:19 所属栏目:百科 来源:网络整理
导读:我们实现一个简单的printf函数(可变参数) #include stdio.h #include stdarg.h void myprintf( const char * format,...){ va_list ap; char c; va_start(ap,format); while (c = *format++ ) { switch (c) { case ' c ' : { char ch = va_arg(ap, int ); pu

我们实现一个简单的printf函数(可变参数)

#include <stdio.h>
#include <stdarg.h>
void myprintf(const char *format,...)
{
    va_list ap;
    char c;
    va_start(ap,format);
    while (c = *format++) {
        switch(c) {
            case 'c': {
                char ch = va_arg(ap,int);
                putchar(ch);
                break;
            }
            schar *p = va_arg(ap,1)">);
                fputs(p,stdout);
                default:
                putchar(c);
        }
    }
    va_end(ap);
}
int main(void)
{
    myprintf("ctsn",1',1)">hello");
    return 0;
}

  要处理可变参数,需要用C标准库的va_list类型和va_start、va_arg、va_end宏,这些定义在stdarg.h头文件中。我们首先对照反汇编分析在调用myprintf函数时这些参数的内存布局。

  

  

  myprintf函数的参数布局

  

  这些参数是从右向左依次压栈的,所以第一个参数靠近着栈顶,第三个参数靠近栈底。这些参数在内存中是连续存放的,每个参数都对齐到4字节边界。第一个和第三个参数都是指针类型,各占4个字节,虽然第二个参数只占一个字节,但为了使第三个参数对齐到4字节边界,所以第二个参数也占4个字节。现在给出一个stdarg.h的简单实现

/* stdarg.h standard header */
#ifndef _STDARG
#define _STDARG
 type definitions 
typedef va_list;
 macros */
#define va_arg(ap,T) 
     (* (T *)(((ap) += _Bnd(T,1)">3U)) - _Bnd(T,1)">3U)))
#define va_end(ap) (void)0
#define va_start(ap,A) 
     (void)((ap) = (char *)&(A) + _Bnd(A,1)">))
#define _Bnd(X,bnd) (sizeof (X) + (bnd) & ~(bnd))
#endif

  这个头文件中的内部宏定义_Bnd(X,bnd)将类型或变量x的长度对齐到bnd+1字节的整数倍,例如_Bnd(char,3U)的值是4, _Bnd(int,3U)也是4。

  在myprintf中定义的va_list ap;其实是一个指针,va_start(ap,format)使ap指向format参数的下一个参数,也就是指向上图中esp+4的位置。然后va_arg(ap,int)把第二个参数的值按int型取出来,同时使ap指向第三个参数,也就是指向上图中esp+8的位置。然后va_arg(ap,char *)把第三个参数的值按char *型取出来,同时使ap指向更高的地址。va_end(ap)在我们的简单实现中不起任何作用,在有些实现中可能把ap改写成无效值,C标准要求在函数返回前调用va_end。

  如果把myprintf中的char ch = va_arg(ap,int);改成char ch = va_arg(ap,char);,用我们的简单实现是没有问题的。但如果改用libc提供的stdarg.h,在编译时会报错:

  

  因此要求char型的可变参数必须按int型来取,这是为了与C标准一致。

  从myprintf的例子可以理解printf的实现原理,printf函数根据第一个参数(格式化字符串)来确定后面有集合参数,分别是什么类型。

  还有一种方法可以确定可变参数的个数,就是在参数列表的末尾传一个Sentinel,例如NULL。execl()就采用了这种方法确定参数的个数。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读