C语言可变参数函数实现 & 分析

C语言可变参数函数实现 & 分析

先给出一个样例程序, 实现一个打印每一个传给它的元素并且换行

/*************************************************************************
    > File Name: va_args_1.c
    > Author: VOID_133
    > ################### 
    > Mail: ################### 
    > Created Time: Sun 17 Apr 2016 09:34:22 PM CST
 ************************************************************************/
#include<stdio.h>
#include<stdarg.h>

void print_list(char *begin, ...)
{
    va_list ap;
    char *p = NULL;
    
    va_start(ap, begin);
    p = va_arg(ap, char *);
    while(p != NULL)
    {
        fputs(p, stdout);
        putchar('\n');
        p = va_arg(ap, char *);
    }
    va_end(ap);
}

int main(void)
{
    print_list(0, "This", "is", "Multi", "Line", NULL);
    return 0;
}

上面的代码已经很清晰了, 下面简单说明一下如何编写一个可变参数函数

1.定义函数, 注意参数列表的第一个参数一定要有,就算没有用到也不能为空, 而且必须有名字, 这个是提供给va_start作为参数的

void print_list(char *begin, ...)

2.在自定义的函数定义 va_list 型变量(ap), 作为一会儿使用的指向变参的指针

3.然后使用 va_start将 (ap)指向 参数列表中的第一个元素

4.然后使用 va_arg,获取 “…” 中的参数, 每使用一次, 都会获取下一个参数的值

5.最后使用 va_end, C标准要求在函数调用结束前使用va_end

变参的原理通过函数调用的原理可以解释通顺

变参实际上就是在函数调用栈帧上压入不确定个数个参数,a

然后一个指针从第一个参数一直移动到最后一个, (注意内存对齐) 即可

压栈的汇编代码如下:

080484a6 <main>:

int main(void)
{
 80484a6:	8d 4c 24 04          	lea    0x4(%esp),%ecx
 80484aa:	83 e4 f0             	and    $0xfffffff0,%esp
 80484ad:	ff 71 fc             	pushl  -0x4(%ecx)
 80484b0:	55                   	push   %ebp
 80484b1:	89 e5                	mov    %esp,%ebp
 80484b3:	51                   	push   %ecx
 80484b4:	83 ec 04             	sub    $0x4,%esp
    print_list(0, "This", "is", "Multi", "Line", NULL);
 80484b7:	83 ec 08             	sub    $0x8,%esp
 80484ba:	6a 00                	push   $0x0
 80484bc:	68 70 85 04 08       	push   $0x8048570
 80484c1:	68 75 85 04 08       	push   $0x8048575
 80484c6:	68 7b 85 04 08       	push   $0x804857b
 80484cb:	68 7e 85 04 08       	push   $0x804857e
 80484d0:	6a 00                	push   $0x0
 80484d2:	e8 74 ff ff ff       	call   804844b <print_list>
 80484d7:	83 c4 20             	add    $0x20,%esp
    return 0;
 80484da:	b8 00 00 00 00       	mov    $0x0,%eax
}
 80484df:	8b 4d fc             	mov    -0x4(%ebp),%ecx
 80484e2:	c9                   	leave  
 80484e3:	8d 61 fc             	lea    -0x4(%ecx),%esp
 80484e6:	c3                   	ret    
 80484e7:	66 90                	xchg   %ax,%ax
 80484e9:	66 90                	xchg   %ax,%ax
 80484eb:	66 90                	xchg   %ax,%ax
 80484ed:	66 90                	xchg   %ax,%ax
 80484ef:	90                   	nop

这里是将参数压栈的代码

    print_list(0, "This", "is", "Multi", "Line", NULL);
 80484b7:	83 ec 08             	sub    $0x8,%esp
 80484ba:	6a 00                	push   $0x0
 80484bc:	68 70 85 04 08       	push   $0x8048570
 80484c1:	68 75 85 04 08       	push   $0x8048575
 80484c6:	68 7b 85 04 08       	push   $0x804857b
 80484cb:	68 7e 85 04 08       	push   $0x804857e
 80484d0:	6a 00                	push   $0x0

待解决问题: 如果用格式化字符串+可变参数中出现了 char 型变量的话, 这个变量要作为int型在va_arg里接受,而不是char, 不然会编译报警告, 并且不能运行目标文件

编译警告:

In file included from va_args.c:9:0:
va_args.c: In function ‘myprintf’:
va_args.c:26:33: warning: ‘char’ is promoted to ‘int’ when passed through ‘...’
                 ch = va_arg(ap, char);
                                 ^
va_args.c:26:33: note: (so you should pass ‘int’ not ‘char’ to ‘va_arg’)
va_args.c:26:33: note: if this code is reached, the program will abort

运行错误:

[1] 14258 illegal hardware instruction (core dumped) ./a.out

 

Leave a Reply

Your email address will not be published. Required fields are marked *

five + eight =

This site uses Akismet to reduce spam. Learn how your comment data is processed.