C Primer Plus 第9章 函數 9.2 ANSI C 的函數原型

9.2.1 產生的問題函數

下面咱們討論幾個使用imax()函數的例子,該函數和imin()相似。在程序清單9.4中的程序以舊的形式聲明函數imax(),而後錯誤的使用該函數。操作系統

程序清單9.4 misuse.c程序prototype

/*misuse.c --不正確的使用函數*/
#include <stdio.h>
int imax();  /*舊式的函數聲明*/
int main(void)
{
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3));
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3.0,5.0));
    return 0;
}
int imax(n,m)
int n,m;
{
    int max;
    if(n>m)
        max=n;
    else
        max=m;
    return max;
}

在第一個printf()中調用函數imax()時漏掉了一個參數,而在第二次調用imax()時使用了浮點參數而不是整數參數。儘管存在這些錯誤,該程序仍能夠編譯執行。code

程序運行時發生了什麼?不一樣操做系統的內部機制不一樣,因此出現錯誤的具體狀況也不相同。當使用PC或VAX時,程序執行過程是這樣的:調用函數首先把參數放在一個被稱爲堆棧(stack)的臨時存儲區域裏,而後被調函數從堆棧中讀取這此參數。可是這兩個過程並無相互協調進行。調用函數根據調用過程當中實際參數類型肯定須要傳遞的數值類型,可是被調函數是根據其形式參數的類型進行數據讀取的。所以,函數調用 imax(3)把一個整數放在堆棧中。當函數imax()開始執行時,它會從堆棧中讀取兩個整數。而實際上只有一個須要的數值被存儲在堆棧中,因此第二個讀取的數據就是當時剛好在堆棧中的其餘數值。字符串

第二次使用函數imax()時,傳遞的是float類型的數值。這時兩個double類型的數值就被放在堆棧中(回憶一下,做爲參數傳遞時float類型數據會被轉換成double類型數據)。而在咱們使用的系統中,這意味着兩個64位的數值,即共128位的數據存儲在堆棧中。由於這個系統中int系統是32位,因此當imax()從堆棧中讀取兩個int類型的數值時,它會讀取出堆棧中前面64位的數據,把這些數據對應於兩個整數,其中較大的一個就是1074266112。原型

9.2.2  ANSI的解決方案編譯器

針對以上的參數錯誤匹配問題,ANSI標準的解決方案是在函數聲明中同時說明所使用的參數類型。即便用函數原型(function prototype)來聲明返回值類型、參數個數以及各參數的類型。爲了表示imax()須要兩個int類型的參數,可使用下面原型中的任意一個進行聲明:io

int imax(int ,int);編譯

int imax(int a,int b);function

第一種形式使用逗號對參數類型進行分隔;而第二種形式在類型後加入了變量名。須要注意的是這此變量名只是虛擬的名字,它們沒必要和函數定義中使用的變量名相匹配。

使用這種函數原型信息,編譯器就能夠檢查函數調用語句是否和其原型聲明一致。好比檢查參數個數是否正確,參數類型是否匹配。若是有一個參數類型不匹配但都是數值類型,編譯器會把實際參數值轉換成和形式參數類型相同的數值。例如 ,會把imax(3.0,5.0)換成imax(3,5).

當使用函數原型時,上例中的程序清單9.4會變成以下程序清單9.5。

程序清單9.5  proto.c程序

/*misuse.c --使用函數原型*/
#include <stdio.h>
int imax(int,int);  /*原型*/
int main(void)
{
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3));
    printf("The maximum of %d and %d is %d.\n",
           3,5,imax(3.0,5.0));
    return 0;
}
int imax(int n,int m)
{
    int max;
    if(n>m)
        max=n;
    else
        max=m;
    return max;
}

當編譯程序清單9.5時,編譯器會給出一個錯誤信息,聲稱調用函數imax()時傳遞的參數太少。咱們用imax(3,5)代替imax(3)後從新進行編譯。這一次並無出現任何錯誤信息。

雖然編譯中沒有出現錯誤信息,可是編譯器給出了一條警告信息,提示doube類型數據被轉換成了int類型的數據,所以可能會損失數據。例如,如下函數調用:

imax(3.9,5.4);等價於語句imax(3,5);

錯誤和警告的不一樣之處在於前者阻止了編譯的繼續然後者不阻止。

9.2.3  無參數和不肯定參數

假設使用如下函數原型:

void printf_name();

這時一個ANSI C 編譯器會假設您沒有用函數原型聲明函數,它就不會進行參數檢查。所以,爲了表示一個函數確實不使用參數,須要在圓括號內加入void關鍵字:

void printf_name(void);

ANSI C 會把上句解釋爲pintf_name()不接受任何參數,所以當對函數進行調用時編譯器就會檢查以保證您確實沒有使用參數。一些函數使用的參數個數是變化的。例如,在printf()中,第一個參數是一個字符串,而其他參數的類型以及參數個數並不固定。對於這種狀況,ANSI C 容許使用不肯定的函數原型。例如,對於printf()可使用下面的原型聲明:

int printf(char *,...);

這種原型表示第一個參數是一個字符串,而其他參數不能肯定。

對於參數個數不肯定的函數,C庫經過stdarg.h頭文件提供了定義該類函數的標準方法。第16章「C預處理器和C庫」詳細講述了有關內容。

9.2.4  函數原型的優勢

函數原型是對語言的有力補充。它可使編譯器發現函數使用時可能出現的錯誤或疏漏

有一種方法能夠不使用函數原型卻保留函數原型的優勢。之因此使用函數原型,是爲了在編譯器編譯第一個調用函數的語句以前向其代表該函數的使用方法。所以,能夠在首次調用某函數以前對該函數進行完整的定義。這樣函數定義部分就和函數原型有着相同的做用。一般對於較小的函數會這樣作:

//下面便是一個函數定義,也是它的原型

int imax(int a,int b)  { return a>b ? a:b;}

int main(void)

{

...

z=imax(x,50);

...

}

相關文章
相關標籤/搜索