1. 程式人生 > >函式的宣告、定義和呼叫

函式的宣告、定義和呼叫

1 函式的宣告與定義

1.1 宣告的概念

    宣告:一般位於標頭檔案,告知編譯器這裡有一個叫xxx(函式名)的函式,作用是讓編譯器知道這個函式的存在。總而言之宣告的功能就是告訴編譯器有這麼個函式,但並不實現。

1.2 定義的概念

    定義:用於實現這個函式,真正在記憶體(堆或棧中)為此函式分配空間。定義一般在原始檔裡,以函式體的形式展現函式的實現過程。

1.3 宣告與定義

    “定義”是指對函式功能的確立,包括指定函式名,函式值型別、形參型別、函式體等,它是一個完整的、獨立的函式單位。而“宣告” 的作用則是把函式的名字、函式型別以及形參型別、個數和順序通知編譯系統,以便在呼叫該函式時系統按此進行對照檢查(例如函式名是否正確,實參與形參的型別和個數是否一致)。
    從程式中可以看到對函式的宣告與函式定義中的函式首部基本上是相同的。因此可以簡單地照寫已定義的函式的首部,再加一個分號,就成為了對函式的“宣告”。在函式宣告中也可以不寫形參名,而只寫形參的型別。
    在C語言中,使用函式原型是ANSI C的一個重要特點。它的作用主要是利用它在程式的編譯階段對呼叫函式的合法性進行全面檢查。函式原型(function prototype)並不等於函式宣告,只能說函式原型(function prototype)是函式宣告的一種形式。
說明:

  1. 以前的C版本的函式宣告方式不是採用函式原型,而只是宣告函式名和函式型別。 如:float add(); 不包括引數型別和引數個數。系統不檢查引數型別和引數個數。新版本也相容這種用法,但不提倡這種用法,因為它未進行全面的檢查。
  2. 實際上,如果在函式呼叫前,沒有對函式作宣告,則編譯系統會把第一次遇到的該函式形式(函式定義或函式呼叫)作為函式的宣告,並將函式型別預設為int 型。如一個max函式,呼叫之前沒有進行函式宣告,編譯時首先遇到的函式形式是函式呼叫”max(a, b)”,由於對原型的處理是不考慮引數名的,因此係統將max()加上int作為函式宣告,即int max(); 因此不少教材說,如果函式型別為整型,可以在函式呼叫前不必作函式宣告。但是使用這種方法時,系統無法對引數的型別做檢查。或呼叫函式時引數使用不當,在編譯時也不會報錯。因此,為了程式清晰和安全,建議都加以宣告為好。
  3. 如果被呼叫函式的定義出現在主調函式之前,可以不必加以宣告。因為編譯系統已經先知道了已定義的函式型別,會根據函式首部提供的資訊對函式的呼叫作正確性檢查。所以可以說函式定義本身也是一種函式宣告。
  4. 如果已在所有函式定義之前,在函式的外部已做了函式宣告,則在各個主呼叫函式中不必對所呼叫的函式再作宣告。

2 函式的呼叫

    在C語言中有兩種函式呼叫方式:傳值呼叫和傳地址呼叫。下面通過兩個簡單的程式介紹一下這兩種函式呼叫方式。值得注意的是在C語言裡並沒有C++所謂的“按引用呼叫”的概念,這裡的傳地址呼叫與C++中引用呼叫實現的功能是一樣的。

下面我們再強調一下兩者的概念:
1. 傳值:實際就是把實參的值賦值給形參,相當於copy。此時對形參的修改,不會影響實參的值 。
2. 傳址:實際是傳值的一種特殊方式,只是他傳遞的是地址,不是普通的賦值,傳地址以後,實參和形參都指向同一個物件,因此對形參的修改會影響到實參。

2.1 傳值呼叫

#include <stdio.h>

void swap(int x,int y)
{
    int temp;
    temp=x;
    x=y;
    y=temp;
    printf("x=%d,y=%d\n",x,y);
}

void main()
{
    int a=6;
    int b=8;
    swap(a,b);
    printf("a=%d,b=%d\n",a,b);
}

執行結果如下圖所示:
傳值呼叫實現結果
    從執行結果上可以看出實參a和b並沒有實現數值交換,實現交換的只有被調函式裡的形參x和y。
    造成這樣結果的原因是我們雖然通過呼叫函式將a和b的值傳遞給了被調函式的形參x和y,即在呼叫函式時隱含地把實參a、b 的值分別賦值給了x、y,x、y也完成了它們之間數值的交換。但是由於函式體內並沒有對、,b進行任何的操作,所以交換的只是x、y變數,並不是a、b。這其實是一次單向的傳遞過程,a、b能傳給x、y,x、y能成功互換其數值。但x、y是定義在函式swap中的區域性變數,當swap()函式執行完畢,其中swap()函式佔用的記憶體資源會被釋放(在main函式中呼叫swap時會給swap開闢一個函式棧,函式返回時就會被釋放),因此被調函式沒有渠道把交換的值傳回給a、b。結果當然是a、b的值就不會發生改變!函式只是把a、b的值通過賦值傳遞給了x、y,函式裡頭操作的只是x、y的值並不是a、b的值。這就是所謂的引數的值傳遞了。

2.2 傳地址呼叫

#include <stdio.h>

void swap(int *x,int *y)
{
    int temp;
    temp=*x;
    *x=*y;
    *y=temp;
    printf("*x=%d,*y=%d\n",*x,*y);
}

void main()
{
    int a=6;
    int b=8;
    swap(&a,&b);
    printf("a=%d,b=%d\n",a,b);
}

傳地址呼叫執行結果
    從函式的介面部分:swap(int *x,int *y),可以看出此時引數x、y都是指標。再看函式呼叫處:swap(&a, &b);,它是將a的地址(&a)代入到x,b的地址(&b)代入到y。同上面的值傳遞一樣,函式呼叫時作了兩個隱含的操作:將&a,&b的值賦值給了x,y,即x=&a;y=&b;。所以傳遞的實參是a和b在記憶體中的地址,那麼在swap中的x就指向main中a的地址,y就指向main中b的地址,此時指標x、y的值已經分別是a、b變數的地址值了,那麼當*p1、*p2被修改時,a、b也會跟著發生變化,因為此時二者佔用了同一塊空間,當任意一者使空間裡的內容發生變化時,二者都會做相同變化。