C语言函数可变长度参数剖析
2013-11-01 14:57
232 查看
C语言中的很多函数的入参被定义为可变参数,最典型的
int printf (const char * fmt, ...)
要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, VA_END, VA_ARG 宏 ,需要包含<stdarg.h>头文件
利用va族函数对不定参数进行解析的过程所示如下:
要了解不定参数的处理方式,就要搞清楚va族函数的实现
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
/* 实质就是一个char型的指针 */
typedef char * va_list;
/* 将指针偏移一个v的长度,指向后面的地址 */
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
/* 根据参数类型,取出后面的数据,强制类型转换 */
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/* 将指针置为NULL */
#define va_end(ap) ( ap = (va_list)0 )
通过解析fmt字符串,得到后面的参数类型和个数,根据参数类型再加上偏移量就可以找到栈中的不定参数了
函数调用和传参的过程所示如下:
将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。
例如
执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:
这样,通过param1的地址就可以计算出param2与param3的地址:
使用不定参函数的注意事项
在C语言中,调用一个可变参数函数时,调用者会对每个参数执行“默认实际参数提升(default argument promotions)”
——float类型的实际参数将提升到double
——char类型的实际参数将提升到int
——short类型的实际参数将提升到int
在没有函数原型的情况下,char与short类型都将被转换为int类型,float类型将被转换为double类型。
——《C语言程序设计》第2版 2.7
类型转换 p36
这样写肯定是不对的:
c = va_arg(ap,char);
因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。上面的式子应该写成:
c = va_arg(ap,int);
——《C陷阱与缺陷》p164
int printf (const char * fmt, ...)
要对其中的可变参数进行处理,就要用到va_list类型和 VA_START, VA_END, VA_ARG 宏 ,需要包含<stdarg.h>头文件
利用va族函数对不定参数进行解析的过程所示如下:
int my_printf(const char * fmt, ...) { va_list struAp; va_start(struAp, fmt); for (; *fmt; ++fmt) { if(*fmt != '%') { PUTC(*fmt); continue; } fmt++; switch (*fmt) { case 'd': { int i = va_arg(struAp,int); PUTC(i); } break; default: break; } } va_end(struAp); }
要了解不定参数的处理方式,就要搞清楚va族函数的实现
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
/* 实质就是一个char型的指针 */
typedef char * va_list;
/* 将指针偏移一个v的长度,指向后面的地址 */
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
/* 根据参数类型,取出后面的数据,强制类型转换 */
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/* 将指针置为NULL */
#define va_end(ap) ( ap = (va_list)0 )
通过解析fmt字符串,得到后面的参数类型和个数,根据参数类型再加上偏移量就可以找到栈中的不定参数了
函数调用和传参的过程所示如下:
将函数参数与函数调用后下一条指令的地址都压入栈中,然后跳到函数的入口地址。
例如
void func(int param1, double param2,int param3){ } int main() { func(3, 1.2, 4); printf("Over\n"); //设指令地址为0x1234 return 0; }
执行f(3, 1.2, 4)的函数调用,进入func函数时的堆栈如下:
这样,通过param1的地址就可以计算出param2与param3的地址:
使用不定参函数的注意事项
在C语言中,调用一个可变参数函数时,调用者会对每个参数执行“默认实际参数提升(default argument promotions)”
——float类型的实际参数将提升到double
——char类型的实际参数将提升到int
——short类型的实际参数将提升到int
在没有函数原型的情况下,char与short类型都将被转换为int类型,float类型将被转换为double类型。
——《C语言程序设计》第2版 2.7
类型转换 p36
这样写肯定是不对的:
c = va_arg(ap,char);
因为我们无法传递一个char类型参数,如果传递了,它将会被自动转化为int类型。上面的式子应该写成:
c = va_arg(ap,int);
——《C陷阱与缺陷》p164
相关文章推荐
- C语言函数的可变长度参数va_arg剖析
- Java 数组的不同定义方式和数组长度为可变参数
- 使用stdarg.h实现可变长度参数
- Scala可变长度参数
- C语言函数入栈顺序与可变参数函数
- scala可变长度参数(二)
- C语言函数中利用指针引用可变参数的方法
- C++ Variadic template(可变长度参数函数)
- JAVA可变长度的参数
- java函数参数后面加三个点——可变长度参数列表
- Java中定义形参长度可变的参数
- 深入了解JAVA可变长度的参数(Varargs)
- Java Object...可变长度的参数(Varargs)详解
- scala可变长度参数(一)
- Scala可变长度参数和:_*使用
- JAVA类型后面跟3个点(可变长度参数列表)
- C语言函数入栈顺序与可变参数函数
- Java高新技术1---增强for循环 ---装箱与拆箱享元设计模式--自己写枚举--可变长度的参数
- 学会JAVA可变长度的参数(Varargs)
- 深入了解JAVA可变长度的参数(Varargs)