函數調用

標籤: 暫無標籤

30

更新時間: 2013-08-29

廣告

函數調用,在程序中是通過對函數的調用來執行函數體的,其過程與其它語言的子程序調用相似。

函數調用 -函數調用的一般形式

  前面已經說過,在程序中是通過對函數的調用來執行函數體的,其過程與其它語言的子程序調用相似。
  C語言中,函數調用的一般形式為:
  函數名(實際參數表)
  對無參函數調用時則無實際參數表。實際參數表中的參數可以是常數,變數或其它構造類型數據及表達式。各實參之間用逗號分隔。
函數調用 -函數調用的方式

無內容
函數調用 -在C語言中,可以用以下幾種方式調用函數:

1. 函數表達式:

  函數作為表達式中的一項出現在表達式中,以函數返回值參與表達式的運算。這種方式要求函數是有返回值的。例如:z=max(x,y)是一個賦值表達式,把max的返回值賦予變數z。
2. 函數語句:

廣告

  函數調用的一般形式加上分號即構成函數語句。例如: printf ("%d",a);scanf ("%d",&b);都是以函數語句的方式調用函數。
3. 函數實參:

  函數作為另一個函數調用的實際參數出現。這種情況是把該函數的返回值作為實參進行傳送,因此要求該函數必須是有返回值的。例如: printf("%d",max(x,y)); 即是把max調用的返回值又作為printf函數的實參來使用的。在函數調用中還應該注意的一個問題是求值順序的問題。所謂求值順序是指對實參表中各量是自左至右使用呢,還是自右至左使用。對此,各系統的規定不一定相同。介紹printf 函數時已提到過,這裡從函數調用的角度再強調一下。
  【例】
  main()
  {
  int i=8;
  printf("%d\n%d\n%d\n%d\n",++i,--i,i++,i--);
  }
  如按照從右至左的順序求值。運行結果應為:
  8
  7
  7
  8
  如對printf語句中的++i,--i,i++,i--從左至右求值,結果應為:
  9
  8
  8
  9
  應特別注意的是,無論是從左至右求值, 還是自右至左求值,其輸出順序都是不變的, 即輸出順序總是和實參表中實參的順序相同。由於Turbo C現定是自右至左求值,所以結果為8,7,7,8。上述問題如還不理解,上機一試就明白了。
  被調用函數的聲明和函數原型
  在主調函數中調用某函數之前應對該被調函數進行說明(聲明),這與使用變數之前要先進行變數說明是一樣的。在主調函數中對被調函數作說明的目的是使編譯系統知道被調函數返回值的類型,以便在主調函數中按此種類型對返回值作相應的處理。
  其一般形式為:
  類型說明符 被調函數名(類型 形參,類型 形參…);
  或為:
  類型說明符 被調函數名(類型,類型…);
  括弧內給出了形參的類型和形參名,或只給出形參類型。這便於編譯系統進行檢錯,以防止可能出現的錯誤。
  例 main函數中對max函數的說明為:
  int max(int a,int b);
  或寫為:
  int max(int,int);
  C語言中又規定在以下幾種情況時可以省去主調函數中對被調函數的函數說明。
  1) 如果被調函數的返回值是整型或字元型時,可以不對被調函數作說明,而直接調用。這時系統將自動對被調函數返回值按整型處理。例8.2的主函數中未對函數s作說明而直接調用即屬此種情形。
  2) 當被調函數的函數定義出現在主調函數之前時,在主調函數中也可以不對被調函數再作說明而直接調用。例如例8.1中,函數max的定義放在main 函數之前,因此可在main函數中省去對max函數的函數說明int max(int a,int b)。
  3) 如在所有函數定義之前,在函數外預先說明了各個函數的類型,則在以後的各主調函數中,可不再對被調函數作說明。例如:
  char str(int a);
  float f(float b);
  main()
  {
  ……
  }
  char str(int a)
  {
  ……
  }
  float f(float b)
  {
  ……
  }
  其中第一,二行對str函數和f函數預先作了說明。因此在以後各函數中無須對str和f函數再作說明就可直接調用。
  4) 對庫函數的調用不需要再作說明,但必須把該函數的頭文件用include命令包含在源文件前部。
函數調用 -函數的嵌套調用

廣告

  C語言中不允許作嵌套的函數定義。因此各函數之間是平行的,不存在上一級函數和下一級函數的問題。但是C語言允許在一個函數的定義中出現對另一個函數的調用。這樣就出現了函數的嵌套調用。即在被調函數中又調用其它函數。這與其它語言的子程序嵌套的情形是類似的。其關係可表示如圖。
  圖表示了兩層嵌套的情形。其執行過程是:執行main函數中調用a函數的語句時,即轉去執行a函數,在a函數中調用b 函數時,又轉去執行b函數,b函數執行完畢返回a函數的斷點繼續執行,a函數執行完畢返回main函數的斷點繼續執行。
  【例】計算s=2∧2!+3∧2!
  本題可編寫兩個函數,一個是用來計算平方值的函數f1,另一個是用來計算階乘值的函數f2。主函數先調f1計算出平方值,再在f1中以平方值為實參,調用 f2計算其階乘值,然後返回f1,再返回主函數,在循環程序中計算累加和。
  long f1(int p)
  {
  int k;
  long r;
  long f2(int);
  k=p*p;
  r=f2(k);
  return r;
  }
  long f2(int q)
  {
  long c=1;
  int i;
  for(i=1;i<=q;i++)
  c=c*i;
  return c;
  }
  main()
  {
  int i;
  long s=0;
  for (i=2;i<=3;i++)
  s=s+f1(i);
  printf("\ns=%ld\n",s);
  }
  在程序中,函數f1和f2均為長整型,都在主函數之前定義,故不必再在主函數中對f1和f2加以說明。在主程序中,執行循環程序依次把i值作為實參調用函數f1求i2值。在f1中又發生對函數f2的調用,這時是把i2的值作為實參去調f2,在f2 中完成求i2!的計算。f2執行完畢把C值(即i2!)返回給f1,再由f1返回主函數實現累加。至此,由函數的嵌套調用實現了題目的要求。由於數值很大,所以函數和一些變數的類型都說明為長整型,否則會造成計算錯誤。
函數調用 -函數調用的實際(彙編)實現

廣告

指針寄存器EBP和ESP

  EBP是所謂的幀指針,指向當前活動記錄的上方(上一個活動記錄的最下方)
  ESP是所謂的棧指針,指向當前活動記錄的最下方(下一個將要插入的活動記錄的最上方)
  這兩個指針的值規定了當前活動記錄的位置
參數傳遞

  將函數參數壓棧:mov eax,dword ptr [n] ;(n為參數變元)
  push eax
函數調用分配空間

  函數調用將執行如下操作:
  ⒈將幀指針壓入棧中:push ebp
  ⒉使得幀指針等於棧指針:mov ebp,esp
  ⒊使棧指針自減,自減得到的內存地址應當能夠(足夠)用來存儲被調用函數的本地狀態:sub esp,0CCh
  注意:0CCh為0xCC,隨著具體函數的不同而不同。
傳入保存狀態

  push ebx ;保存ebx寄存器的值
  push esi ;保存esi寄存器的值
  push edi ;保存edi寄存器的值
裝入edi

  lea edi,[ebp-0CCh] ;0cch是當前活動記錄的大小。
  EDI是目的變址寄存器。
恢復傳入的保存狀態

  00411417 pop edi
  00411418 pop esi
  pop ebx
棧指針上移,恢復空間

  add esp,0CCh
函數返回釋放空間

  當函數返回時,編譯器和硬體將執行如下操作:
  ⒈使棧指針等於幀指針: mov esp,ebp
  ⒉從棧中將舊的幀指針彈出: pop ebp
  ⒊返回:ret
一個函數調用的實例

  ;void function(int n)
  ;{
  push ebp
  mov ebp,esp
  sub esp,0CCh
  push ebx
  push esi
  push edi
  lea edi,[ebp-0CCh]
  mov ecx,33h
  mov eax,0CCCCCCCCh
  rep stos dword ptr es:[edi]
  ;char a=1;
  mov byte ptr [a],1
  ;if(n==0)return;
  cmp dword ptr [n],0
  jne function+2Ah (4113CAh)
  jmp function+77h (411417h)
  ;printf("%d\t(0x%08x)\n",n,&n);
  mov esi,esp
  lea eax,[n]
  push eax
  mov ecx,dword ptr [n]
  push ecx
  push offset string "%d\t(0x%08x)\n" (415750h)
  call dword ptr [__imp__printf (4182B8h)]
  add esp,0Ch
  cmp esi,esp
  call @ILT+305(__RTC_CheckEsp) (411136h)
  ;function(n-1);
  mov eax,dword ptr [n]
  sub eax,1
  push eax
  call function (411041h)
  add esp,4
  ;printf("----%d\t(0x%08x)\n",n,&n);
  mov esi,esp
  lea eax,[n]
  push eax
  mov ecx,dword ptr [n]
  push ecx
  push offset string "----%d\t(0x%08x)\n" (41573Ch)
  call dword ptr [__imp__printf (4182B8h)]
  add esp,0Ch
  cmp esi,esp
  call @ILT+305(__RTC_CheckEsp) (411136h)
  ;}
  pop edi
  pop esi
  pop ebx
  add esp,0CCh
  cmp ebp,esp
  call @ILT+305(__RTC_CheckEsp) (411136h)
  mov esp,ebp
  pop ebp
  ret

廣告

廣告