关于c中可变参数的一点学习
2015-02-04 10:19
211 查看
今天在看windows c++编程的时候看到了一些有关可变参数的东西,比如va_list va_start va_end va_arg 不懂,查阅了一下。
举一个简单易懂的例子
#include <stdarg.h>
#include <stdio.h>
float average(int n_values, ... )
{
va_list var_arg; //用于访问参数列表的未确定部分,通过va_start来初始化
int count;
float sum = 0;
/*
准备访问可变参数
*/
va_start( var_arg, n_values ); //第一个参数为va_list变量的名字,第二个参数为省略号前最后一个有名字的参数。初始化过程把va_list变量设置为指向可变参数的第一个参数
/*
添加取自可变参数列表的值
*/
for( count = 0; count < n_values; count += 1 )
{
sum += va_arg( var_arg, int ); //可变参数必须从头到尾顺序逐个访问。在va_arg中注意不可以指定错误的类型。
}
/*
完成处理可变参数
*/
va_end( var_arg );
return sum / n_values;
}
int main()
{
float a = average(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
printf("The average: %.1f\n", a);
return 0;
}//此例子为转载
在这里 我们想实现不定个数的平均数,所以这里需要用到可变参数,
第一步 可变参数表示
用三个点…来表示,查看printf()函数和scanf()函数的声明:
int printf(const char *, ...);
int scanf(const char *, ...);
这三个点用在宏中就是变参宏(Variadic Macros),默认名称为__VA_ARGS__。如:
#define WriteLine(...) { printf(__VA_ARGS__); putchar('\n');}
再WriteLine("MoreWindows");
考虑下printf()的返回值是表示输出的字节数。将上面宏改成:
#define WriteLine (...) printf(__VA_ARGS__) + (putchar('\n') != EOF ? 1: 0);
这样就可以得到WriteLine宏的返回值了,它将返回输出的字节数,包括最后的'\n'。如下例所示i和j都将输出12。
int i = WriteLine("MoreWindows");
WriteLine("%d", i);
int j = printf("%s\n", "MoreWindows");
WriteLine("%d", j);
第二步 如何处理va_list类型
函数内部对可变参数都用va_list及与它相关的三个宏来处理,这是实现变参参数的关键之处。
在<stdarg.h>中可以找到va_list的定义:
typedef char * va_list;
再介绍与它关系密切的三个宏要介绍下:va_start(),va_end()和va_arg()。
同样在<stdarg.h>中可以找到这三个宏的定义:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_end(ap) ( ap = (va_list)0 )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
其中用到的_INTSIZEOF宏定义如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
来分析这四个宏:
va_end(ap)这个最简单,就是将指针置成NULL。
va_start(ap,v)中ap = (va_list)&v + _INTSIZEOF(v)先是取v的地址,再加上_INTSIZEOF(v)。_INTSIZEOF(v)就有点小复杂了。( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )全是位操作,看起来有点麻烦,其实不然,非常简单的,就是取整到sizeof(int)。比如sizeof(int)为4,1,2,3,4就取4,5,6,7,8就取8。对x向n取整用C语言的算术表达就是((x+n-1)/n)*n,当n为2的幂时可以将最后二步运算换成位操作——将最低
n - 1个二进制位清 0就可以了。
va_arg(ap,t)就是从ap中取出类型为t的数据,并将指针相应后移。如va_arg(ap, int)就表示取出一个int数据并将指针向移四个字节。
因此在函数中先用va_start()得到变参的起始地址,再用va_arg()一个一个取值,最后再用va_end()收尾就可以解析可变参数了。
其实此处INSTANCEOF的作用就是实现向后字节对其,比如v是int,sizeof(int)=4 那么就是向后实现4字节对其,
定义:
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
1 我们知道对于IX86,sizeof(int)一定是4的整数倍,所以~(sizeof(int) - 1) )的值一定是
右面[sizeof(n)-1]/2位为0,整个这个宏也就是保证了右面[sizeof(n)-1]/2位为0,其余位置
为1,所以_INTSIZEOF(n)的值只有可能是4,8,16,......等等,实际上是实现了字节对齐。
2 #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
的目的在于把sizeof(n)的结果变成至少是sizeof(int)的整倍数,这个一般用来在结构中实现按int的倍数对齐。
如果sizeof(int)是4,那么,当sizeof(n)的结果在1~4之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在5~8时,
_INTSIZEOF(n)的结果会是8;当sizeof(n)的结果在9~12时,_INTSIZEOF(n)的结果会是12;……总之,会是sizeof(int)的倍数。
举一个简单易懂的例子
#include <stdarg.h>
#include <stdio.h>
float average(int n_values, ... )
{
va_list var_arg; //用于访问参数列表的未确定部分,通过va_start来初始化
int count;
float sum = 0;
/*
准备访问可变参数
*/
va_start( var_arg, n_values ); //第一个参数为va_list变量的名字,第二个参数为省略号前最后一个有名字的参数。初始化过程把va_list变量设置为指向可变参数的第一个参数
/*
添加取自可变参数列表的值
*/
for( count = 0; count < n_values; count += 1 )
{
sum += va_arg( var_arg, int ); //可变参数必须从头到尾顺序逐个访问。在va_arg中注意不可以指定错误的类型。
}
/*
完成处理可变参数
*/
va_end( var_arg );
return sum / n_values;
}
int main()
{
float a = average(10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
printf("The average: %.1f\n", a);
return 0;
}//此例子为转载
在这里 我们想实现不定个数的平均数,所以这里需要用到可变参数,
第一步 可变参数表示
用三个点…来表示,查看printf()函数和scanf()函数的声明:
int printf(const char *, ...);
int scanf(const char *, ...);
这三个点用在宏中就是变参宏(Variadic Macros),默认名称为__VA_ARGS__。如:
#define WriteLine(...) { printf(__VA_ARGS__); putchar('\n');}
再WriteLine("MoreWindows");
考虑下printf()的返回值是表示输出的字节数。将上面宏改成:
#define WriteLine (...) printf(__VA_ARGS__) + (putchar('\n') != EOF ? 1: 0);
这样就可以得到WriteLine宏的返回值了,它将返回输出的字节数,包括最后的'\n'。如下例所示i和j都将输出12。
int i = WriteLine("MoreWindows");
WriteLine("%d", i);
int j = printf("%s\n", "MoreWindows");
WriteLine("%d", j);
第二步 如何处理va_list类型
函数内部对可变参数都用va_list及与它相关的三个宏来处理,这是实现变参参数的关键之处。
在<stdarg.h>中可以找到va_list的定义:
typedef char * va_list;
再介绍与它关系密切的三个宏要介绍下:va_start(),va_end()和va_arg()。
同样在<stdarg.h>中可以找到这三个宏的定义:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_end(ap) ( ap = (va_list)0 )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
其中用到的_INTSIZEOF宏定义如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
来分析这四个宏:
va_end(ap)这个最简单,就是将指针置成NULL。
va_start(ap,v)中ap = (va_list)&v + _INTSIZEOF(v)先是取v的地址,再加上_INTSIZEOF(v)。_INTSIZEOF(v)就有点小复杂了。( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )全是位操作,看起来有点麻烦,其实不然,非常简单的,就是取整到sizeof(int)。比如sizeof(int)为4,1,2,3,4就取4,5,6,7,8就取8。对x向n取整用C语言的算术表达就是((x+n-1)/n)*n,当n为2的幂时可以将最后二步运算换成位操作——将最低
n - 1个二进制位清 0就可以了。
va_arg(ap,t)就是从ap中取出类型为t的数据,并将指针相应后移。如va_arg(ap, int)就表示取出一个int数据并将指针向移四个字节。
因此在函数中先用va_start()得到变参的起始地址,再用va_arg()一个一个取值,最后再用va_end()收尾就可以解析可变参数了。
其实此处INSTANCEOF的作用就是实现向后字节对其,比如v是int,sizeof(int)=4 那么就是向后实现4字节对其,
定义:
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
1 我们知道对于IX86,sizeof(int)一定是4的整数倍,所以~(sizeof(int) - 1) )的值一定是
右面[sizeof(n)-1]/2位为0,整个这个宏也就是保证了右面[sizeof(n)-1]/2位为0,其余位置
为1,所以_INTSIZEOF(n)的值只有可能是4,8,16,......等等,实际上是实现了字节对齐。
2 #define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
的目的在于把sizeof(n)的结果变成至少是sizeof(int)的整倍数,这个一般用来在结构中实现按int的倍数对齐。
如果sizeof(int)是4,那么,当sizeof(n)的结果在1~4之间是,_INTSIZEOF(n)的结果会是4;当sizeof(n)的结果在5~8时,
_INTSIZEOF(n)的结果会是8;当sizeof(n)的结果在9~12时,_INTSIZEOF(n)的结果会是12;……总之,会是sizeof(int)的倍数。
相关文章推荐
- 今天学习了关于C++ 中可变参数个数函数的使用!
- 关于静态导入,装箱与拆箱,枚举,以及可变参数的学习
- 我的java学习笔记(11)关于装箱、参数可变方法和枚举类型
- stdarg Macro 关于可变参数
- 关于MySQL的一点学习纪录
- 可变参数学习笔记
- 写我的读书笔记关于可变参数
- 可变参数学习笔记(转贴老迈的文章)
- 关于main参数和inp.c代码阅读的一点笔记
- 关于学习编程的一点想法
- 关于51单片学习的一点心得
- 关于java学习的一点感言
- 关于 Delphi 参数传递方式的一点研究
- CSDN C/C++电子杂志第一期 之 可变参数学习笔记
- 关于DNN Module开发学习以来的一点总结
- 计算机学习笔记2:关于微程序的一点己见
- 可变参数学习笔记
- 可变参数学习
- 关于学习 Flex 的一点建议(转载)
- CSDN C/C++电子杂志第一期 之 可变参数学习笔记