歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 關於C語言不定參數的研究

關於C語言不定參數的研究

日期:2017/3/1 9:07:23   编辑:Linux編程

一、 學習過程

編寫程序如下:

編譯連接並用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",45,123,8958,'Q');

}

void printf(char *des, ...)

{

/*first sure the length of string des*/

int len=0;

int i=0;

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

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

int intValueLength=1;

int signalNum=0;

/*calculate length of stirng des */

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

{

len++;

i++;

}

i=0;

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

{

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

{

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

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

{

/*here show integer value*/

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*/

int reValue=showIntValue;

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

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

i+=2;

parap++;

intValueLength=1;

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

while(reValue/10!=0)

{

intValueLength++;

reValue/=10;

}

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

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

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

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

showp++;

while(intValueLength!=0)

{

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

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

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

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

showp++;

}

/*showp+=intValueLength;*/

}

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

{

/*here show charactor value*/

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

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

parap++;

showp++;

i+=2;

}

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

{

*(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);

i++;

showp++;

}

}

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

{

*(char far *)(0xb8000000+160*10+80+showp+showp)=*(int *)(*(int *)(_BP+4)+i);

i++;

showp++;

}

}

}

int pow(int index,int power)

{

int finalValue=1;

if(power==0);

else

{

while(power!=0)

{

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?如果是更大的參數怎麼辦?

Copyright © Linux教程網 All Rights Reserved