一、什么是可变参数
我们在C语言编程中有时会遇到一些参数个数可变的函数,例如printf()函数,其函数原型为:
int printf( const char* format, ...);
它除了有一个参数format固定以外,后面跟的参数的个数和类型是可变的(用三个点“…”做参数占位符),实际调用时可以有以下的形式: printf("%d",i);
printf("%s",s);
printf("the number is %d ,string is:%s", i, s);
以上这些东西已为大家所熟悉。但是究竟如何写可变参数的C函数呢?
二、不使用库函数
#include <stdio.h> int sum(int n, ...) { int i; int sum = 0; int *p; p=&n + 1; for(i=0;i<n;i++) { sum += *(p+i); } return sum; } int main(void) { int result = sum(3, 1, 2); printf("%d\n", result); return 0; }
分析:c语言中,堆栈的栈底位于高地址,堆栈的栈顶位于低地址。而函数调用时的压栈顺序默认为从右向左,也就是2先进栈,然后是1,最后是3。可变长参数先进栈,位于高地址。固定参数n后入栈,位于低地址,并且与可变参数连续。所以得到n的地址后,就可以得到变长参数的首地址。
三、使用库函数
#include "stdio.h" #include "stdarg.h" void simple_va_fun(int start, ...) { va_list arg_ptr; int nArgValue =start; int nArgCout=0; //可变参数的数目 va_start(arg_ptr,start); //以固定参数的地址为起点确定变参的内存起始地址。 //即变参的内存起始地址为固定参数的地址加上固定参数所占字节的长度 printf("%x %x\n", (int)arg_ptr, &start); //例如 0x0008 0x0004 do { ++nArgCout; printf("the %d th arg: %d. ",nArgCout,nArgValue); //输出各参数的值 nArgValue = va_arg(arg_ptr,int); //得到下一个可变参数的值 } while(nArgValue != -1); printf("\n"); return; } int main(int argc, char* argv[]) { simple_va_fun(100,-1); simple_va_fun(100,200,-1); return 0; }
四、自定义myprintf
#include "stdio.h" #include "stdlib.h" void myprintf(char* fmt, ...) //一个简单的类似于printf的实现,//参数必须都是int 类型 { char* pArg=NULL; //等价于原来的va_list char c; pArg = (char*) &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; }