天空之城
发新话题
文本浏览 我的话题(本区) 打印

【转】扒开可变参数虚伪的外衣~~~

【转】扒开可变参数虚伪的外衣~~~

可变参数是C的一个重要特色,其实也很好用。但好象很少有人讨论这个。我们常用的
printf(const char* fmt,...)就是可变参数的问题,当你想从写库的时候,我相信你就会用
到了。
这问题以前困饶了我好久,问了一些同学,也上网查了一些资料,上面总是仅仅举几个用法的例子,叫你用<stdarg.h>(标准参数头文件),我就郁闷了,大家都会用,我想知道那“。。。”到底是什么意思撒,没有答案。在一本书里我看到了一句话“可变参数涉及到C的调用约定”,我想明白了,我相信一定有跟我一样困惑的人,所以我想把我的一些想法告诉大家,有什么错误,还请指正。
1、“...”语法代表不知道有多少个参数,也不知道是什么类型。
2、C的调用约定:
参数传递约定:堆栈传递,反向压栈(右边的最先压栈),以long型保存。
寄存器约定,你可以不理解的(用到C和汇编接口的朋友可以看下)
寄存器的保存约定:eax,ecx,edx有调用者保存,ebx,edi,esi,由被调用
者保存(如果被调用者用到的话),至于esp,ebp由硬件完成。
被调用者的参数压在调用者的栈帧上见下图:
|—————————————
| : |
| : |
|—————————————
| 参数n |
|—————————————
| : |
|—————————————
| 参数3 |
|—————————————
| 参数2 |
|—————————————
| 参数1 |
|—————————————
| 返回地址 | 地
|————————————— ^ 址
|保存的EBP | | 增
|————————————— | 大
| : | |
|—————————————
网上的那些例子总是参照printf()的那固定模式,用第一个参数,也加上<stdarg.h>
里面的那几个宏,有病呢,以前好郁闷,谁都会用,我想知道怎么回事,又没人答。这里
让那几个宏见鬼去吧。
先举一个简单的例子:
#include <stdio.h>
void becall(char a,long b)
{
printf("b=%d\n",*((long*)(&a+4)));
}
void main(void)
{
becall('w',5);
}
对上面的分析一下:
重点理解*((long*)(&a+4))是什么意思,这是关键。
从上面压栈约定我们已经知道,参数b的值在堆栈中的地址比参数a在堆栈中的地址
要大1(倘若我们把他们全看作是long型的话)。
又因为a是char型的,又因为所有参数是以long 型压栈的,所以,(&a+4)就是参数
b的地址,应为b是long型的,所以我们把他强制转化为long型指针,再取内容,即",*((long*)(&a+4)))。
现在我再举一个例子说明她的用法:
#include <stdio.h> /*用到printf()*/
/*nr_arg是指“...”所代表的参数个数*/
long math_action(char* str1 ,short nr_arg,char action,...)
{
long return_val;
short i;
return_val=0;
i=1; /*i指向第一个参数*/
printf(str1);
if(nr_arg!=0)
return_val=*((long*)(&action+4)); /*第一个参数传给return_val*/
nr_arg--;
i++;
while(nr_arg)
{
switch(action)
{
case '+': return_val=return_val+(*((long*)(&action+4*i)));break;
case '-': return_val=return_val-(*((long*)(&action+4*i)));break;
case '*': return_val=return_val*(*((long*)(&action+4*i)));break;
case '/': return_val=return_val/(*((long*)(&action+4*i)));break;
default: break;
}
nr_arg--;
i++;
}
return return_val;
}
void main(void)
{
long x;
x=math_action("加法计算:\n" ,5,'+',1,2,3,4,5);
printf("1+2+3+4+5=%d\n",x);
x=math_action("减法计算:\n" ,6,'-',1,2,3,4,5,6);
printf("1-2-3-4-5-6=%d\n",x);
x=math_action("乘法计算:\n" ,7,'*',1,2,3,4,5,6,7);
printf("1*2*3*4*5*6*7=%d\n",x);
x=math_action("除法计算:\n" ,4,'/',8,2,2,2);
printf("8/2/2/2=%d\n",x);
}
我相信聪明的你理解了上面的这个函数,现在完全可以把不用库中的printf()了,它只不过在根据第一个字符串指针取起内容对其解析而已,遇到‘%’或‘\’就对后面的一个字符或者一个数字串,作出相应的解释,也许我们写出来的代码会有一些问题,但我们却可以说我们懂她,而不仅仅知道会用而已。
“可变参数”很好用,现在她衣服都被你扒光了,兄弟还等什么呢?


// #inlcude <stdio.h>
// #include <stdarg.h>
//如果不想太麻烦 用stdarg.h也不错 下面是例子
void QcsOutputString(const char* fmt,...)
{
char msg[1024];
va_list ap;
va_start(ap,fmt);
vsprintf(msg,fmt,ap);
va_end(ap);
//下面可以将msg输出到你想要输出的地方
}
性别:男-当前离线 xzp:



※本文所有权属于作者和 天空之城 社区共同所有,未经同意,禁止转载!相关资料仅供交流使用,请在下载后24小时内删除!※

※如有关资料侵犯您的合法权益,敬请告知管理人员,本站会在24小时内删除!※

郑重声明:本人发的主题帖大部分都是转载。如果侵犯了某人的权益,请及时与我取得联系,本人会立即将其删除。

TOP

支持一下
性别:保密-当前离线 kernel32:



※本文所有权属于作者和 天空之城 社区共同所有,未经同意,禁止转载!相关资料仅供交流使用,请在下载后24小时内删除!※

※如有关资料侵犯您的合法权益,敬请告知管理人员,本站会在24小时内删除!※


请完善您的签名档

TOP

发新话题

---------------------------------------------------------------------------------------
本论坛所有文章为会员所发布,会员拥有该内容的所有权力及责任,转载时请注明出处!
本站资源大部分来自于互联网,请在下载后进行杀毒! 如有关资料侵犯您的合法权益,敬请告知,本站会在24小时内删除!
管理员:JueJue QQ:4131669(请直击主题), QQ交流群:48557033 邮箱:tanjunge(at)live.com(用@代替at).
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论!