汇编网首页登录博客注册
musicvs的学习博客
博客首页博客互动【做检测题】论坛求助

我的博客

个人首页 |  我的文章 |  我的相册 |  我的好友 |  最新访客 |  文章收藏 |  论坛提问 |  友情链接 |  给我留言  
图片载入中
  •  像幻想的梦想,接近梦想的理想
  • 『姓名』:木头                
  • 『性别』:男    『发送消息
  • 个人说明:希望能成为一个程序员^_^
  • 详细信息『加为好友』
学习动态

[2008-10-07 12:07] 推荐博文 综合研究 研究试验5 函数如何接收不定数量的参数

这个试验里,王老师要我们研究3个问题.

第一个:研究下面的程序

void showchar(char a,int b);
main()
{
   showchar('a',2);
}

void showchar(char a,int b)
{
   *(char far *)(0xb8000000+160*10+80)=a;
   *(char far *)(0xb8000000+160*10+81)=b;
}

问我们,main函数是如何给showchar传递参数的?showchar是如何接受参数.
从程序的汇编代码中可以看出,main函数是将要传递的参数入栈,而入栈的顺序是从最后一个参数开始的,这也正好让我们想到,showchar函数一定是通过出栈的方式取得参数的吧.哈,事实也是如此.

第二个问题:看下面的程序
void showchar(int,int,...);

main()
{
   showchar(8,2,'a','b','c','d','e','f','g','h');
}

void showchar(int n,int color,...)
{
   int a;
   for (a=0;a!=n;a++)
   {
      *(char far *)(0xb8000000+160*10+80+a+a)=*(int *)(_BP+8+a+a);
      *(char far *)(0xb8000000+160*10+81+a+a)=color;
   }
}
王老师问我们,showchar函数是如何知道要显示多少个字符的?printf函数是如何知道有多少个参数的?
首先,看到这句void showchar(int,int,...);  就让我们想到,那个...省略号应该就是试验5的标题所写的不定量的参数了.
后面的showchar(8,2,'a','b','c','d','e','f','g','h');就是说有8个字符,而这些字符的颜色属性是2.
而for循环里面的当然就是向显存写入字符的ascii码和颜色属性了.

那showchar函数是如何知道要显示多少个字符的?
很明显,n就是说明了有8个字符了,但是具体是如何操作的呢?
关键就是这句代码了:
 *(char far *)(0xb8000000+160*10+80+a+a)=*(int *)(_BP+8+a+a);
下面我就说说我的想法咯:
1.从debug中可以看到,程序中的数据段和栈段的段地址是相等的
2.*(int *)(_BP+8+a+a);就是相当于DS:[_BP+8+a+a]
3._BP+8是使DS:[_BP+8+a+a]最初指向存放字符a的内存单元.
4.a+a是干什么的呢?从for语句中可以看出,a+a是以偶数的方式递增的,即 0,2,4,6,8,10,12,14,16.
而当a+a=16即a=8时,不进行循环,所以我们只要看0,2,4,6,8,10,12,14这8种(刚好就是8个字符咯).
5.因为参数的传递是利用入栈的方式来进行的,所以一个字符是占据了一个字单元的空间的(原因是al中存放字符的ascii码,ah为0,而入栈的时候把整个ax入栈,所以就占了一个字单元的空间).
呵呵~到这里大家都知道了吧,为什么要用a+a?因为要使DS:[]指向每一个字符,所以每次加2,就指向下一个内存单元,也就是下一个字符了.
6.同样,这句话的0xb8000000+160*10+80+a+a,是用来指向下一个显存的内存单元的.

不过,还是有一个疑问,为什么在写程序的时候就知道要用_BP+8呢?
首先,最后入栈的参数是0002和0008,这里占了4个字节.然后在查看汇编代码时,才能知道,后面又进行了2次入栈操作,所以加起来就是8个字节,_BP+8就刚好能够指向字符'a'.
那,是不是有一个规律呢?

我发现:
1.每个函数的开始都会有这个2条指令
push bp
mov bp,sp
main函数里有,showchar函数里也有.
2.定义变量的时候,如果用int来定义变量,然后直接赋值的话,就跟试验3所说的一样,先分配一个空间,然后赋值的时候,用ds:[bp-02]的形式来指向变量的空间;
而如果是定义int变量之后,使用for语句,在for语句中给变量赋值.
如:
int a;
for (a=0;a!=n;a++)
那么,就会使用si寄存器来存放变量的值.
既然要用到si,那当然要先把si入栈,就像子程序一样,要用到什么寄存器,就把这些寄存器入栈,最后再出栈,以免修改了寄存器的内存.
所以,这里就有一个push si.

综合来说,
除了把字符参数入栈之外,还有4个入栈操作:0002和0008的入栈,push bp以及push si.(占8个字节)
所以,为了指向字符'a',就要使用_BP+8,然后为了循环指向字符a~h,就要使用_BP+8+a+a.
这就是*(int *)(_BP+8+a+a);的作用了...

哈,暂时研究到这里...后面再继续~^_^
(写了这么多,不知道大家会不会看不下去呢,嘻嘻~)

------------------------------------------------------------------
第一次编辑                             2008年10月7日12:11:45
                                             By 木头
------------------------------------------------------------------

******************************************************************
还有2个问题没有解决,就是上面问的:printf函数是如何知道有多少个参数的?
还有就是这个试验的第三问了:实现一个简单的printf函数,只需要支持"%c,%d"即可.

如果能知道printf函数是如何知道有多少个参数的话,第三问就差不多能解决了.
刚开始我以为可以仿照第二问的程序,用printf(...,int)这种方法,结果证明--我错了!
我又想,是不是用c语言中的字符串数组呢?但是无奈我的c语言功底实在是...(可以忽略).

不知道各位有什么方法解决第三问呢?希望能给我点提示...

------------------------------------------------------------------
第二次编辑                             2008年10月8日19:55:53
                                             By 木头
------------------------------------------------------------------





感谢一位朋友的提醒(可惜没有留名字)...我找到了我整个研究试验5的一个错误--我忽略了调用showchar函数时,会使用到call指令,而call指令会使IP入栈,从而,我以前所写的就错误了!
我还要几天要考试,就不把我的心得写出来了...如果有需要一起讨论的朋友,请加我QQ:357382814  谢谢~^_^

第三次编辑                             2009年1月3日 20:39
                                             By 木头
------------------------------------------------------------------
评论次数(12)  |  浏览次数(1639)  |  类型(木头心情) |  收藏此文  | 

[  lwbfq   发表于  2008-10-08 17:50  ]

支持一下 呵呵

[  musicvs   发表于  2008-10-08 19:51  ]

呵呵~还没完成...

[  abob   发表于  2008-10-22 14:43  ]

用力的踩踩~~~

[  longhun   发表于  2008-10-28 10:26  ]

我还没学到。。。。。。等我遇到想不懂你再教我吧,先支持一下

[  mxl800   发表于  2008-11-26 09:05  ]

还没学到,我是过来问问题的

[  游客   发表于  2008-12-30 21:59  ]

按博主对_BP+8的理解,是不能理解第一个问题的?
第一个问题对char a字符读取是_BP+4,但前面只有PUSH BP入栈,应该是_BP+2.为什么是_BP+4呢?
博主再仔细看一下第二个问题中SI是到底是用来干什么的?
(何况push si指令是在mov bp,sp的后面,是不会影响BP的值的.)
定义的整数型变量又放在哪了?

[  musicvs   发表于  2009-01-01 17:40  ]

谢谢楼上的...等我放假之后要再好好想想,下个星期考试,呵呵

[  musicvs   发表于  2009-01-03 20:34  ]

还是忍不住重新看了一遍,我找到了我错在哪了...非常感谢啊~

[  游客   发表于  2009-02-27 20:49  ]

_BP寄存器默认的段是_SS,而不是博主说的_DS,博主该复习一下了

[  游客   发表于  2009-06-02 22:20  ]

我的debug中数据段和栈段不是一样的,这是为什么呢?
直接运行可以成功显示,但是单步跟踪,只是显示空格

[  caicaihong   发表于  2010-04-20 18:10  ]

想问一下,综合研究5中,怎么去找并且去判断%d和%c呢?我想了很久想不明白,希望你能给点提示。

[  wind_asm   发表于  2010-05-09 17:03  ]

一直有个地方不明白:
*(char far *)(0xb8000000+160*10+80+a+a)=*(int *)(_BP+8+a+a); 
为什么 前面是 char ,后面是int呢?
一个双字节的 数据怎么传给 单字节?

 
 请输入验证码  (提示:点击验证码输入框,以获取验证码