关于c语言不定参数的研究

一、 学习过程

编写程序如下:

编译连接并用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函数的基础上来改动。

下面是网上找的代码:

  1 void printf(char *,...);
  2
  3 int pow(int, int);
  4
  5
  6
  7 main()
  8
  9 {
 10
 11 /*printf("I think this is interesting :%c and %c and %c",0x61,0x62,0x63);*/
 12
 13
 14
 15 printf("No.%d,%d,%d,this is me %c ing yuan",45,123,8958,‘Q‘);
 16
 17 }
 18
 19
 20
 21 void printf(char *des, ...)
 22
 23 {
 24
 25 /*first sure the length of string des*/
 26
 27 int len=0;
 28
 29 int i=0;
 30
 31 int showp=0; /*define the point of showing chars*/
 32
 33 int parap=0; /*define of parameter position in stack*/
 34
 35 int intValueLength=1;
 36
 37 int signalNum=0;
 38
 39 /*calculate length of stirng des */
 40
 41 while(des[i]!=‘/0‘)
 42
 43 {
 44
 45 len++;
 46
 47 i++;
 48
 49 }
 50
 51 i=0;
 52
 53
 54
 55 while(des[i]!=‘/0‘)
 56
 57 {
 58
 59 if(des[i]==‘%‘)
 60
 61 {
 62
 63 /*check type of value user want to show.*/
 64
 65 if(des[i+1]==‘d‘)
 66
 67 {
 68
 69 /*here show integer value*/
 70
 71 int showIntValue=*(int *)(_BP+6+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*/
 72
 73 int reValue=showIntValue;
 74
 75 /* *(int far *)(0xb8000000+160*10+80+showp+showp)=showIntValue;
 76
 77 *(int far *)(0xb8000000+160*10+81+showp+showp)=2;*/
 78
 79 i+=2;
 80
 81 parap++;
 82
 83 intValueLength=1;
 84
 85 /*here we calculate the length of integer value we want to show ,and then we can sure the next value show position*/
 86
 87 while(reValue/10!=0)
 88
 89 {
 90
 91 intValueLength++;
 92
 93 reValue/=10;
 94
 95 }
 96
 97 /*first calculate the length of unmber and show  every sigal positon number of Integer  */
 98
 99 signalNum = showIntValue/pow(10,--intValueLength);
100
101 *(char far *)(0xb8000000+160*10+80+showp+showp)=signalNum+48; /*show the highest signal number*/
102
103 *(char far *)(0xb8000000+160*10+81+showp+showp)=2;
104
105 showp++;
106
107 while(intValueLength!=0)
108
109 {
110
111 showIntValue=showIntValue-signalNum*pow(10,intValueLength);
112
113 signalNum= showIntValue/pow(10,--intValueLength);
114
115 *(char far *)(0xb8000000+160*10+80+showp+showp)=signalNum+48; /*show the highest signal number*/
116
117 *(char far *)(0xb8000000+160*10+81+showp+showp)=2;
118
119 showp++;
120
121 }
122
123 /*showp+=intValueLength;*/
124
125 }
126
127 else if (des[i+1]==‘c‘)
128
129 {
130
131 /*here show charactor value*/
132
133 *(char far*)(0xb8000000+160*10+80+showp+showp)=*(int *)(_BP+6+parap+parap); /*value of _BP and distance address of  CALL order*/
134
135 *(char far*)(0xb8000000+160*10+81+showp+showp)=2;
136
137 parap++;
138
139 showp++;
140
141 i+=2;
142
143 }
144
145 else /*direct show char value in string des*/
146
147 {
148
149 *(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);
150
151 i++;
152
153 showp++;
154
155 }
156
157 }
158
159 else /*also direct to show char in des*/
160
161 {
162
163 *(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);
164
165 i++;
166
167 showp++;
168
169 }
170
171 }
172
173 }
174
175
176
177 int pow(int index,int power)
178
179 {
180
181 int finalValue=1;
182
183 if(power==0);
184
185 else
186
187 {
188
189 while(power!=0)
190
191 {
192
193 finalValue=finalValue*index;
194
195 power--;
196
197 }
198
199 }
200
201 return finalValue;
202
203 }

二、 解决的问题

(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?如果是更大的参数怎么办?

时间: 2024-08-27 06:26:10

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

C语言不定参数

最近,遇到一个c语言的不定参数问题.其实,对于c语言的不定参数问题,只需要三个函数就可以搞定了.这三个函数的头文件是<stdarg.h>,其实下面的三个函数都是一个宏定义(macro).    这三个函数是:    void va_start(va_list ap, last);    type va_arg(va_list ap, type);    void va_end(va_list ap);    如果需要进行其他的一些操作,可以查看一下man手册进行查询.    在这三个函数解释之

C语言可变参数函数实现原理

一.可变参数函数实现原理 C函数调用的栈结构: 可变参数函数的实现与函数调用的栈结构密切相关,正常情况下C的函数参数入栈规则为__stdcall, 它是从右到左的,即函数中的最右边的参数最先入栈. 本文地址:http://www.cnblogs.com/archimedes/p/variable-parameter.html,转载请注明源地址. 例如,对于函数: void fun(int a, int b, int c) { int d; ... } 其栈结构为 0x1ffc-->d 0x200

不定参数的应用

不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多.除了格式化输出之外,我实在没看到多少应用.主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替换它.尽管如此,既然大家对它比较感兴趣,我就简单总结一下它的使用和需要注意的常见问题.原理:刚学C语言的时候,一般人都会首先接触printf函数.通过这个函数,你可以打印不定个数的变量到屏幕,如:printf("%d", 3);printf("%d,%d",3,4);

Golang的不定参数

在很多语言中都提供了不定参数和函数重载以及函数式语言中得闭包来提高函数的灵活性.如果使用过fmt包里的函数,那么你就已经接触到了Golang的不定参数了.那么如何定义一个自己的不定参数的函数呢? 一.函数定义 首先来看如何定义一个不定参数的函数: func YourFun(v... interface{}){ } 该函数定义,定义了一个接受任何数目任何类型参数的函数.这里特殊的语法是三个点"...",在一个变量后面加上三个点后,表示从该处开始接受不定参数,不同于python,golan

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

package main import "fmt" func main() { myfunc1(88, 42, 12, 56) //传递不定数量的参数 myfunc2([]int{88, 42, 12, 56}) //传递一个数组切片 } func myfunc1(args ...int) { //接受不定数量的参数,这些参数的类型全部是int for _, arg := range args { fmt.Println(arg) } } func myfunc2(args []int

实现自己的不定参数的函数

不定参数在一些特殊场合下是很有用的,例如在字符串的格式化合成,像printf()函数.日志输出等场合都很容易想到这样的结构. 其实,这种方法在C语言里就已经存在了,也不是什么新奇特的东西,一般要实现这样的功能,需要以下几个函数/类型组成: va_list .va_start.va_avg.va_end 头文件:stdarg.h 先来看两个例子: 例1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 uint GetSum(uint nD1,...) {     va_list

C语言——可变参数

http://blog.chinaunix.net/space.php?uid=25304914&do=blog&id=3066441 一.是什么 我们学习C语言时最经常使用printf()函数,但我们很少了解其原型.其实printf()的参数就是可变参数,想想看,我们可以利用它打印出各种类型的数据.下面我们来看看它的原型: int printf( const char* format, ...); 它的第一个参数是format,属于固定参数,后面跟的参数的个数和类型都是可变的(用三个点&

c++不定参数函数

不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多.除了格式化输出之外,我实在没看到多少应用.主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重载函数也足以替换它.尽管如此,既然大家对它比较感兴趣,我就简单总结一下它的使用和需要注意的常见问题. 原理 刚学C语言的时候,一般人都会首先接触printf函数.通过这个函数,你可以打印不定个数的变量到屏幕,如: printf("%d", 3);printf("%d,%d",3,4)

了解golang的不定参数(... parameters),这一篇就够了

在实际开发中,总有一些函数的参数个数是在编码过程中无法确定的,比如我们最常用的fmt.Printf和fmt.Println: fmt.Printf("一共有%v行%v列\n", rows, cols) fmt.Println("共计大小:", size) 当你需要实现类似的接口时,就需要我们的不定参数出场了. golang的不定参数 顾名思义,不定参数就是一个占位符,你可以将1个或者多个参数赋值给这个占位符,这样不管实际参数的数量是多少,都能交给不定参数来处理,我们