va_list、va_start、va_arg、va_end

Posted on 2010-07-08 13:48 小火球 阅读(325) 评论(0)  编辑 收藏 引用 所属分类: C/C++
简介

va_listva_argva_end是为了处理变参数的函数而做的宏定义,这些定义会因为平台(cpu、操作系统)和环境(编译系统)的不同而有所不同。

简单原理:编译系统编译时,会将函数的参数依次放到栈中,这样根据固定参数的地址以及固定参数给出的相关信息很容易得到可变参数的个数、类型、值。注意一点,这些或者是固定参数给出的信息,虽然不是直接给出的;或者是程序写作者自我约定。得到了可变参数,剩下的处理和普通函数一样了。

 1#include <stdio.h>;
 2#include <stdlib.h>;
 3#include <time.h>;
 4#include <varargs.h>;
 5#include <stdarg.h>;
 6#include <errno.h>;
 7using namespace std;
 8
 9void arg_cnt(int cnt, )
10{
11    int nValue = 0;
12    va_list arg_ptr;
13    va_start(arg_ptr,cnt);
14    for (int i = 0; i < cnt;++i)
15    {
16        nValue = va_arg(arg_ptr,int);
17        cout << nValue << endl;
18    }

19}

20int main(int argc,char *argv[])
21{
22    int int_size = _INTSIZEOF(int);
23    cout << "int_size = " << int_size << endl;
24    arg_cnt(5,1,2,3,4);
25    getchar();
26    return 0;
27}


两个小知识(转)

(1)       win32 vc++编译

内存分配由高址向低址进行;参数调用时,参数入栈顺序:从最后一个开始,直到第一个。

(2)       内存对齐

分为结构成员内存对齐和栈内存对齐。

前者要求:字、双字和四字在自然边界上不需要在内存中对齐。(对字、双字和四字来说,自然边界分别是偶数地址、可以被4整除的地址、和可以被8整除的地址。)一个字或双字操作数跨越了4字节边界,或者一个四字操作数跨越了8字节边界,被认为未对齐的。

后者要求:总保持对齐,而且对齐在4字节边界上。

两者区别:前者是可以通过工具控制对齐边界的,如在vc++中就可以通过控制选项控制边界;而后者是不能控制的,必须对齐的,毕竟栈的效率太影响到程序性能了。

宏定义

以下是vc++6.0定义在stdarg.h文件中的关于x86平台的部分宏定义:

typedef char *  va_list;

#define _INTSIZEOF(n)   ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )

#define va_start(ap,v)  ( ap = (va_list)&v + _INTSIZEOF(v) )

#define va_arg(ap,t)    ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )

#define va_end(ap)      ( ap = (va_list)0 )

结合上例,调用AveInt(4,2,4,6,8),当运行了va_start(myvalist,num);之后,根据定义myvalist =(va_list)&num+_INTSIZEOF(i),这样myvalist指向第一个可变参数2;第一次运行va_arg(myvalist,int);后,先让myvalist+= _INTSIZEOF(int)指向下一个可变参数4,然后在取出前面的可变参数2进行处理;依此类推。


 

由于是可变参数,对编译器来说,检查类型一般比较困难,所以,编译器对类型检查不严格,容易出现由于程序写作者的疏忽导致的错误。例如,可变参数中有个int型的,然而在函数中把这个int型参数当作char *进行处理,就可能导致内存越界等错误。不得不说的是,如果采用WINDOWS窗口,那么这段程序不能执行。望高手能帮我解决这个问题

posts - 28, comments - 3, trackbacks - 0, articles - 0

Copyright © 小火球