深度剖析C语言可变参数列表
2015-11-29 18:42
232 查看
C语言可变参数函数分析与实现
1.可变参数的函数声明:
void PrintFloats ( int amount, ...);[/code]
注:声明中函数的第一个参数名必须是可知的(如例amout参数名开始,而非直接...开始)因为要通过宏确定其传入参数起始地址及个数等问题。
2.使用可变参数函数的具体实现过程:
/* va_start example */ #include <stdio.h> #include <stdarg.h> void PrintFloats ( int amount, ...) { int i; double val; printf ("Floats passed: "); va_list vl; //定义va_list 变量用于确定参数 va_start(vl,amount); //确定函数传入参数的第一个地址 for (i=0;i<amount;i++) { val=va_arg(vl,double);//相继的去处下一个参数,然后进行操作 printf ("\t%.2f",val); } va_end(vl); //结束标志,这个宏只是看起来和va_start 对称而已 printf ("\n"); } int main () { PrintFloats (3,3.14159,2.71828,1.41421); return 0; }[/code]
3.可变参数的内部实现
相关类型的原型(在代码中右击跳转到定义):
typedef char * va_list;
#define va_start _crt_va_start
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define _crt_va_end(ap) ( ap = (va_list)0 )
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
具体分析:
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
这个宏通过类型转换取得参数v的地址。也就是如上例子中的&amount
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
由参数的传递过程来看,这里的n就是上边的v也就是&amount
那么这个式子就是求参数的某个大小了。:
#define _INTSIZEOF(n) 对于上式amount 为int类型有:
( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
=((4 + 4 - 1) & ~(4 - 1) )
=((7 ) & ~(3 )
= 0111 & ~(0011)
= 0111 & ~(1100)
= 0100 = 4(这不正是int的字节对齐数么)
#define _INTSIZEOF(n) 假设传入参数为char类型:
( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
=((1 + 4 - 1) & ~(4 - 1) )
=((4 ) & ~(3 )
= 0100 & ~(0011)
= 0100 & ~(1100)
= 0100 = 4(这不正是x86下的默认字节齐数么)
可看出这个只是什么东东了? 它就是传说中的字节对齐数处理。由于传入参数压栈是由此规矩搞定,那么现在使用我们也就需要用到他了。
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
有以上的分析,对此式子有:ap = (char*)(&amount)+(amount参数的字节对齐数)
//也就是下一个变量的地址了。
#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
所以,实际上,该表达式是反悔了当前地址的变量,而将ap指向下一个地址。
#define _crt_va_end(ap) ( ap = (va_list)0 )
这个就简单了,设置ap = (char*)0; 也就是设置为空。其实这句是可有可无的,只是和start对称,程序的完整性。。或者你也可以理解为他是安全着想(然而影响并没有什么)
至此,我们已经将所有相关的宏进行解读剖析完成。可变参数也就成为了我们使用的一大利器。
4.扩展
另外补充一点的是,这个参数中,最重要的就是对字节对齐的处理了。。这个问题是平台相关的问题了。#elif defined(_M_IX86)
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
这里对应的系统情况下,默认对齐取得是int.所以这也就解释了另外一个问题。(如下例)
以下是一个简单的程序片段
int ADD(char a,...)
{
int val;
cout << &val << endl;
int main()
{
int a = 'a';
cout << &a << endl;
char res = ADD('a','b','b','b','a',-1);
这个问题就是说,对于用到可变参数的地方,参数传递是按照每个参数分别计算字节对齐((1补齐为4)+4=8),而后压栈的,而非一起处理字节对齐而压栈(此情形时应当差距4(1+1--》补齐为4)字节).
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
注:这个真心有点坑。原来不能直接对于图片进行复制粘贴进来,搞得我重新补充/
相关文章推荐
- C++解引用与箭头操作符重载
- C++ 重载自增和自减操作符
- C++实现的简单日期类
- JNI初体验,JNIEnv类型分析与介绍
- 八数码宽度优先算法
- CPP Study
- C/C++中的函数参数传递机制(zz)
- C++primer plus第六版课后编程练习答案9.4
- java与C++多态实现比较
- C++primer plus第六版课后编程练习答案9.2
- C++primer plus第六版课后编程练习答案9.1
- C语言基础篇(三)
- 堆排序主要思想 -- 代码实现(C语言)
- github上c++开源项目
- 位图法排序
- C语言基础篇(二)
- [转载] 循环队列的定义、入队、出队等操作 C++代码实现
- C++ string和stringstream用法总结(转载)
- C语言闰年判断函数
- Rocksdb源码剖析一----Rocksdb概述与基本组件