一、 学习过程

编写程序如下:

编译连接并用debug加载,观察main函数的内容:

Showchar函数的内容:

观察发现,main函数要传递两个参数‘a’和2,在汇编代码中是先将2赋给ax,再将ax入栈,然后将a赋给ax,将ax入栈。在showchar函数中,程序将sp赋给bp,再从bp+4处取出形参a赋给al,再将al中的值赋给b800:690h,然后再从bp+6处取出形参b赋给al,再将al中的值赋给b800:691h。可见main函数给showchar传递参数是把要传递的值赋给ax,再将ax入栈,且如果有多个要传递的值,是由后往前将参数入栈。Showchar函数接收参数是将sp赋给bp,然后由bp+4找到栈中存储的参数a,由bp+6找到栈中存储的的参数b,为什么是bp+4和bp+6呢?因为程序在将两个参数入栈后,call指令将showchar的地址入栈占2个字节,在showchar中将bp入栈又占2个字节,所以要由bp+4找到第一个参数的地址。那么我对此提出三个问题:

(1) main函数将char型数据和int型数据入栈是占2个字节,那么如果是float型或者long int型、double型、long double型等超过2字节的变量类型怎么办?

(2) Showchar函数将栈中取出的参数赋给al,为什么2是int型也只赋给一个字节的al?如果是更大的参数怎么办?

(3) 我们注意到这一个指令

是把al赋给es:[bx],是不是所有非ds的段寄存器都要像这样写?

对于第一个问题,我们把程序中的char和int改成float和double看看:

编译连接,用debug查看,main函数为:

Showchar函数为:

发现main函数的入栈值为:4008、0000、0000、0000、4006、6666.

再看showchar函数的内容,查资料发现int 35h的作用是读取中断向量,但是不知道它的具体功能,inc si和les ax,[06fb];int 39的作用是什么呢?

这里我对于c语言的一些语句在汇编里的实现还是有的不理解,但是这不是我们研究的重点,既然暂时弄不懂,就先跳过这个问题。

再看第三个问题,发现所有es作段地址的指令都是如上格式,ds作段寄存器的指令都把ds省略了。

再来看下一个程序:

编译连接,用debug加载查看,main函数为:

Showchar函数为:

观察C语言的showchar函数可以发现:第一个参数n是要显示的参数数量,第二个参数color是要显示的参数颜色,之后的就是要显示的参数。Showchar函数通过参数n来知道要显示多少个字符。然后通过循环来调用寄存器从栈中提取参数。

但是printf函数的参数是要直接输出的,没有一个参数是告诉它下面有多少个参数。但是printf里面是要输入%c或者%d,那么函数是通过统计%c和%d的数量来判断要输出多少参数的吗?我们写一个printf函数来看看:

编译连接并用debug加载有:

这里是将参数1和2入栈,再入栈194,然后执行printf函数,那么194有什么作用呢?查阅资料知,程序将%c和%d等符号放在偏移地址0194处,结尾加0,通过统计该地址处的%个数来确定要输出的字符数量。所以peintf函数和showchar函数的区别就是showchar函数参数个数已给出而printf函数是要根据%c或%d个数来确定参数个数而已。那么我们要实现简单的printf函数,可以在showchar函数的基础上来改动。

下面是网上找的代码:

 void printf(char *,...);

 int pow(int, int);

 main()

 {

 /*printf("I think this is interesting :%c and %c and %c",0x61,0x62,0x63);*/

 printf("No.%d,%d,%d,this is me %c ing yuan",,,,'Q');

 }

 void printf(char *des, ...)

 {

 /*first sure the length of string des*/

 int len=;

 int i=;

 int showp=; /*define the point of showing chars*/

 int parap=; /*define of parameter position in stack*/

 int intValueLength=;

 int signalNum=;

 /*calculate length of stirng des */

 while(des[i]!='/0')

 {

 len++;

 i++;

 }

 i=;

 while(des[i]!='/0')

 {

 if(des[i]=='%')

 {

 /*check type of value user want to show.*/

 if(des[i+]=='d')

 {

 /*here show integer value*/

 int showIntValue=*(int *)(_BP++parap+parap); /*here, we show understand that define one char point value, we just push the point value into stack, but not the string value*/

 int reValue=showIntValue;

 /* *(int far *)(0xb8000000+160*10+80+showp+showp)=showIntValue;

 *(int far *)(0xb8000000+160*10+81+showp+showp)=2;*/

 i+=;

 parap++;

 intValueLength=;

 /*here we calculate the length of integer value we want to show ,and then we can sure the next value show position*/

 while(reValue/!=)

 {

 intValueLength++;

 reValue/=;

 }

 /*first calculate the length of unmber and show  every sigal positon number of Integer  */

 signalNum = showIntValue/pow(,--intValueLength);

 *(char far *)(0xb8000000+*++showp+showp)=signalNum+; /*show the highest signal number*/

 *(char far *)(0xb8000000+*++showp+showp)=;

 showp++;

 while(intValueLength!=)

 {

 showIntValue=showIntValue-signalNum*pow(,intValueLength);

 signalNum= showIntValue/pow(,--intValueLength);

 *(char far *)(0xb8000000+*++showp+showp)=signalNum+; /*show the highest signal number*/

 *(char far *)(0xb8000000+*++showp+showp)=;

 showp++;

 }

 /*showp+=intValueLength;*/

 }

 else if (des[i+]=='c')

 {

 /*here show charactor value*/

 *(char far*)(0xb8000000+*++showp+showp)=*(int *)(_BP++parap+parap); /*value of _BP and distance address of  CALL order*/

 *(char far*)(0xb8000000+*++showp+showp)=;

 parap++;

 showp++;

 i+=;

 }

 else /*direct show char value in string des*/

 {

 *(char far *)(0xb8000000+*++showp+showp)=*(int *)(*(int *)(_BP+)+i);

 i++;

 showp++;

 }

 }

 else /*also direct to show char in des*/

 {

 *(char far *)(0xb8000000+*++showp+showp)=*(int *)(*(int *)(_BP+)+i);

 i++;

 showp++;

 }

 }

 }

 int pow(int index,int power)

 {

 int finalValue=;

 if(power==);

 else

 {

 while(power!=)

 {

 finalValue=finalValue*index;

 power--;

 }

 }

 return finalValue;

 }

二、 解决的问题

(1) 使用es+偏移地址时,查看指令,段寄存器会独自占一条指令。

(2) Main函数是如何给showchar传递参数的?showchar是如何接受参数的?

答:main函数将参数入栈,showchar用bp寄存器在栈中提取参数。

(3) showchar函数是如何知道要显示多少个字符的?printf是如何知道有多少个参数的?

答:showchar函数是通过第一个参数n知道要显示字符数量的,printf是通过第一个字符串中%c和%d的数量来知道要显示字符数量的。

三、 未解决的问题

(1) main函数将char型数据和int型数据入栈是占2个字节,那么如果是float型或者long int型、double型、long double型等超过2字节的变量类型怎么办?

(2) Showchar函数将栈中取出的参数赋给al,为什么2是int型也只赋给一个字节的al?如果是更大的参数怎么办?

关于c语言不定参数的研究的更多相关文章

  1. C语言不定参数

    最近,遇到一个c语言的不定参数问题.其实,对于c语言的不定参数问题,只需要三个函数就可以搞定了.这三个函数的头文件是<stdarg.h>,其实下面的三个函数都是一个宏定义(macro).  ...

  2. C语言函数不定参数实现方式

    函数如何实现不定参数: 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这种情况,提出了指针参数来解决问题. (1)va_ ...

  3. go语言基础之不定参数的传递

    1.不定参数的传递 示例1: package main //必须有一个main包 import "fmt" func myfunc(tmp ...int) { for _, dat ...

  4. golang中不定参数与数组切片的区别

    package main import "fmt" func main() { myfunc1(, , , ) //传递不定数量的参数 myfunc2([], , , }) //传 ...

  5. C技巧:结构体参数转成不定参数

    下面这段程序是一个C语言的小技巧,其展示了如何把一个参数为结构体的函数转成一个可变参数的函数,其中用到了宏和内建宏"__VA_ARGS__",下面这段程序可以在GCC下正常编译通过 ...

  6. 不定参数函数原理以及实现一个属于自己的printf函数

    一.不定参数函数原理 二.实现一个属于自己的printf函数 参考博文:王爽汇编语言综合研究-函数如何接收不定数量的参数

  7. printf不定参数

    title: printf不定参数 tags: C ARM date: 2018-10-21 12:14:58 --- 不定参数的传递 函数调用时参数传递是使用堆栈来实现的,参数入栈顺序是从右向左,在 ...

  8. c++不定参数函数

    不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多.除了格式化输出之外,我实在没看到多少应用.主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替 ...

  9. Go语言 可变参数

    最近与同事讨论时,提到Go语言的可变参数,之前没有总结过相关知识点,今天我们介绍一下Go语言的可变参数. 可变参数(Variable Parameters):参数数量可变的函数称之为可变参数函数,主要 ...

随机推荐

  1. Java线程Dump分析工具--jstack【转载】

    jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使 ...

  2. libvirt 基于C API基本使用案例

    玩开源分享,需要有干到底的精神,今晚随便逛逛技术论坛突发有感;Ruiy不足之处,需跟进了; 最近变的较懒了,干活有点没劲,也不怪干来干去收获不大,缺少鼓励! 现在玩的技术大多是上不了台面了,想过没,你 ...

  3. spring中@value注解需要注意

    首先,@value需要参数,这里参数可以是两种形式:@Value("#{configProperties['t1.msgname']}")或者@Value("${t1.m ...

  4. wikioi 1044 拦截导弹 (1999年NOIP全国联赛提高组)

    题目描述 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度.某 ...

  5. cocos2dx lua binding ,cocos2dx 绑定lua测试

    前面2篇分别简单介绍 手动bind C++ 类和lua:http://blog.csdn.net/chenee543216/article/details/12074771 使用tolua++简化工作 ...

  6. Java程序员可能犯的3个常见SQL错误

    概述:Java程序员不仅要具备扎实的Java编程能力,在日常的工作当中往往还要涉及到其他语言的基础知识,尤其是SQL.那么哪些常见的SQL错误是程序员们容易犯的呢?让我们一起来看看吧! 你可能看到Ja ...

  7. Hbase设计实战

    Hbase设计实战 本文通过一个游戏公司客户实际案例的讲解,分析了 Hbase 表设计及开发在实际案例中的运用,对比了不同的 Hbase 设计考量对客户端访问模式及检索性能的差异.读者通过案例中 Hb ...

  8. Linux crontab 命令详解(含配置文件路径)

    编辑/etc/crontab 文件配置cron cron 服务每分钟不仅要读一次/var/spool/cron内的所有文件,还需要读一次/etc/crontab,因此我们配置这个文件也能运用cron服 ...

  9. cogs 线型网络(状压dp)

    /* 需要好大的空间..... 而且lowbit理解的不是很好 先放到博客里 以后慢慢研究 */ #include<iostream> #include<cstdio> #in ...

  10. nyoj 37

    //nyoj  37 代码有点乱,和最长公共子序列很像,只是多加了一个数组,之前调用函数, 一直超时,可是我看了下,为什么用一个整形数组存放倒置字符串 竟然可以AC,我测试了下数据,打印出来的是一串地 ...