C語言入門學習——函式的使用
函式的概述
C 程式是由函式組成的,我們寫的程式碼都是由主函式 main()開始執行的。函式是 C 程式的基本模組,是用於完成特定任務的程式程式碼單元。
從函式定義的角度看,函式可分為系統函式和使用者定義函式兩種:(1)系統函式,即庫函式:這是由編譯系統提供的,使用者不必自己定義這些函式,可以直接使用它們,如我們常用的列印函式printf()。
(2)使用者定義函式:用以解決使用者的專門需要。
為什麼要使用函式呢?
1)函式的使用可以省去重複程式碼的編寫。
如果程式中需要多次使用某種特定的功能( 求兩個數的最大值 ),直接寫在main()裡,程式碼的重複率很比較高,同時,程式碼給人的感覺會很冗餘,如下:
#include <stdio.h> int main() { // 操作1 …… // …… int a1 = 10, b1 = 20, c1 = 0; if(a1 > b1){ c1 = a1; }else{ c1 = b1; } // 操作2 …… // …… int a2 = 11, b2 = 21, c2 = 0; if(a2 > b2){ c2 = a2; }else{ c2 = b2; } // …… return 0; }
使用函式後,降低程式碼的重複率,如下:
#include <stdio.h> // 求兩數的最大值 int max(int a, int b) { if(a > b){ return a; }else{ return b; } } int main() { // 操作1 …… // …… int a1 = 10, b1 = 20, c1 = 0; c1 = max(a1, b1); // 呼叫max() // 操作2 …… // …… int a2 = 11, b2 = 21, c2 = 0; c2 = max(a2, b2); // 呼叫max() // …… return 0; }
2)函式可以讓程式更加模組化,從而有利於程式的閱讀,修改和完善。
假如我們編寫一個實現以下功能的程式:讀入一行數字;對數字進行排序;找到它們的平均值;打印出一個柱狀圖。
如果我們把這些操作直接寫在main()裡,這樣可能會給使用者感覺程式碼會有點凌亂。但,假如我們使用函式,這樣可以讓程式更加清晰、模組化。
#include <stdio.h> int main() { float list[50]; // 這裡只是舉例,函式還沒有實現 readlist(list,50); sort(list,50); average(list,50); bargraph(list,50); return 0; }
這裡我們可以這麼理解,程式就像公司,公司是由部門組成的,這個部門就類似於C程式的函式。預設情況下,公司就是一個大部門( 只有一個部門的情況下 ),相當於C程式的main()函式。如果公司比較小( 程式比較小 ),因為任務少而簡單,一個部門即可( main()函式 )勝任。但是,如果這個公司很大( 大型應用程式 ),任務多而雜,如果只是一個部門管理( 相當於沒有部門,沒有分工 ),我們可想而知,公司管理、運營起來會有多混亂,不是說這樣不可以運營,只是這樣不完美而已,如果根據公司要求分成一個個部門( 根據功能封裝一個一個函式 ),招聘由行政部門負責,研發由技術部門負責等,這樣就可以分工明確,結構清晰,方便管理,各部門之間還可以相互協調。
接下來我們一起學習函式:函式的定義,函式的呼叫,函式的宣告。
函式的定義
函式定義的一般形式:
返回型別 函式名( 形式引數列表 )
{
資料定義部分;
執行語句部分;
}
例子如下圖:
這裡還有幾點說明:
1)函式名:理論上是可以隨意起名字,最好起的名字見名知意,應該讓使用者看到這個函式名字就知道這個函式的功能。注意,函式名的後面有個圓換號(),代表這個為函式,不是普通的變數名。
2)形參列表:在定義函式時指定的形參,在未出現函式呼叫時,它們並不佔記憶體中的儲存單元,因此稱它們是形式引數或虛擬引數,簡稱形參,表示它們並不是實際存在的資料,所以,形參裡的變數不能賦值。
int max(int a = 10, int b = 20) // error, 形參不能賦值
{
}
在定義函式時指定的形參,必須是,型別+變數的形式:
///1: right, 型別+變數
int max(int a, int b)
{
}
///2: error, 只有型別,沒有變數
int max(int, int)
{
}
///3: error, 只有變數,沒有型別
int a, int b;
int max(a, b)
{
}
在定義函式時指定的形參,可有可無,根據函式的需要來設計,如果沒有形參,圓括號內容為空,或寫一個void關鍵字:
// 沒形參, 圓括號內容為空
int max()
{
}
// 沒形參, 圓括號內容為void關鍵字
int max(void)
{
}
3)函式體:花括號{ }裡的內容即為函式體的內容,這裡為函式功能實現的過程,這和以前的寫程式碼沒太大區別,以前我們把程式碼寫在main()函式裡,現在只是把這些寫到別的函式裡。
4)返回型別:函式的值是指函式被呼叫之後,執行函式體中的程式段所取得的並返回給主調函式的值。函式的返回值是通過函式中的return語句獲得的,return後面的值也可以是一個表示式。
a)儘量保證return語句中表達式的值和函式返回型別是同一型別。
int max() // 函式的返回值為int型別
{
int a = 10;
return a;// 返回值a為int型別,函式返回型別也是int,匹配
}
b)如果函式返回的型別和return語句中表達式的值不一致,則以函式返回型別為準,即函式返回型別決定返回值的型別。對數值型資料,可以自動進行型別轉換。
int max() // 函式的返回值為int型別
{
double a = 10.1f;
return a;// 返回值a為double型別,它會轉為int型別(a = 10)再返回
}
如果函式返回的型別和return語句中表達式的值不一致,而它又無法自動進行型別轉換,程式則會報錯。
c)return語句的另一個作用為中斷return所在的執行函式,類似於break中斷迴圈,switch語句一樣。
int max()
{
return 1;// 執行到,函式已經被中斷,所以下面的return 2無法被執行到
return 2;// 沒有執行
}
d)如果函式帶返回值,return後面必須跟著一個值,如果函式沒有返回值,函式名字的前面必須寫一個void關鍵字,這時候,我們寫程式碼時也可以通過return中斷函式(也可以不用),只是這時,return後面不帶內容( 分號“;”除外)。
void max()// 必須要有void關鍵字
{
return; // 中斷函式,這個可有可無
}
函式的呼叫
定義函式後,我們需要呼叫此函式才能執行到這個函式裡的程式碼段。這和main()函式不一樣,main()為編譯器設定好自動呼叫的主函式,無需人為呼叫,我們都是在main()函式裡呼叫別的函式,一個 C 程式裡有且只有一個main()函式。
我們有這麼一個簡單程式:
#include <stdio.h>
int main()
{
printf("this is for test\n");
return 0;
}
接著,我們將這個列印的操作封裝在自定義函式裡,這個函式即沒返回值,也沒形參:
#include <stdio.h>
void print_test()
{
printf("this is for test\n");
}
int main()
{
print_test(); // print_test函式的呼叫
return 0;
}
這個程式執行的效果,和上面的是等價的。
程式的執行流程:
1)進入main()函式
2)呼叫print_test()函式:
a) 它會在main()函式的前尋找有沒有一個名字叫“print_test”的函式定義;
b) 如果找到,接著檢查函式的引數,這裡呼叫函式時沒有傳參,函式定義也沒有形參,引數型別匹配;
c) 開始執行print_test()函式,這時候,main()函式裡面的執行會阻塞( 停 )在print_test()這一行程式碼,等待print_test()函式的執行。
3)print_test()函式執行完( 這裡列印一句話 ),main()才會繼續往下執行,執行到return 0, 程式執行完畢。
C語言中,函式呼叫的一般形式為:
函式名(實際引數表)
1)實際引數表,常稱“實參”, 這裡要區別好形參和實參,函式定義時圓括號裡的是形參,函式呼叫時圓括號裡的是實參。
a)如果是呼叫無參函式,則不能加上“實參”,但括號不能省略。
// 函式的定義
void test()
{
}
int main()
{
// 函式的呼叫
test(); // right, 圓括號()不能省略
test(250); // error, 函式定義時沒有引數
return 0;
}
b)如果實參表列包含多個實參,則各引數間用逗號隔開。
// 函式的定義
void test(int a, int b)
{
}
int main()
{
int p = 10, q = 20;
test(p, q); // 函式的呼叫
return 0;
}
c)實參與形參的個數應相等,型別應匹配(相同或賦值相容)。實參與形參按順序對應,一對一地傳遞資料。d)實參可以是常量、變數或表示式,無論實參是何種型別的量,在進行函式呼叫時,它們都必須具有確定的值,以便把這些值傳送給形參。所以,這裡的變數是在圓括號( )外面定義好、賦好值的變數。
// 函式的定義
void test(int a, int b)
{
}
int main()
{
// 函式的呼叫
int p = 10, q = 20;
test(p, q); // right
test(11, 30-10); // right
test(int a, int b); // error, 不應該在圓括號裡定義變數
return 0;
}
2)函式的返回值
a)如果函式定義沒有返回值,函式呼叫時不能寫void關鍵字,呼叫函式時也不能接收函式的返回值。
// 函式的定義
void test()
{
}
int main()
{
// 函式的呼叫
test(); // right
void test(); // error, 函式呼叫時不能寫void關鍵字
int a = test(); // error, 函式定義根本就沒有返回值
return 0;
}
b)如果函式定義有返回值,這個返回值我們根據使用者需要可用可不用,但是,假如我們需要使用這個函式返回值,我們需要定義一個匹配型別的變數來接收。
// 函式的定義, 返回值為int型別
int test()
{
}
int main()
{
// 函式的呼叫
int a = test(); // right, a為int型別
int b;
b = test(); // right, 和上面等級
char *p = test(); // error, p為char *, 函式返回值為int, 型別不匹配
int = test(); // error, 必須定義一個匹配型別的變數來接收返回值,
// int只是型別,沒有定義變數
int test();// error, 必須定義一個匹配型別的變數來接收返回值,
// int只是型別,沒有定義變數
return 0;
}
下面,我們寫一個完整例子:
#include <stdio.h>
/****************************
功能:求兩個數的最大值
引數:
x, y: 需要比較的兩個整數
返回值:兩個數的最大值
******************************/
int max(int x, int y)
{
return x>y ? x : y;
}
int main()
{
int a = 10, b = 25, num_max = 0;
num_max = max(a, b); // 函式的呼叫
printf("num_max = %d\n", num_max);
return 0;
}
當呼叫max(a, b)時,實參變數(a, b)對形參變數(x, y)的進行資料傳遞( “值傳遞” )。只有在發生函式呼叫時,函式max中的形參這時候才會被分配記憶體單元,以便接收從實參傳來的資料。在呼叫結束後,形參所佔的記憶體單元也被釋放。同時,把函式的返回值賦給main()裡的num_max變數。
關於形參和實參的幾點說明:
1)形參出現在函式定義中,在整個函式體內都可以使用,離開該函式則不能使用。
2)實參出現在主調函式中,進入被調函式後,實參變數也不能使用。
3)實參變數對形參變數的資料傳遞是“值傳遞”,即單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。
4)在呼叫函式時,編譯系統臨時給形參分配儲存單元。呼叫結束後,形參單元被釋放。
5)實參單元與形參單元是不同的單元。呼叫結束後,形參單元被釋放,函式呼叫結束返回主調函式後則不能再使用該形參變數。實參單元仍保留並維持原值。因此,在執行一個被呼叫函式時,形參的值如果發生改變,並不會改變主調函式中實參的值。
函式的宣告
在一個函式中呼叫另一個函式(即被呼叫函式)需要具備哪些條件呢?
1)首先被呼叫的函式必須是已經存在的函式。
#include <stdio.h>
int main()
{
int a = 10, b = 25, num_max = 0;
num_max = max(a, b); // 函式的呼叫,這個函式不存在
printf("num_max = %d\n", num_max);
return 0;
}
編譯程式會報錯:
2)如果使用庫函式,包含所需要標頭檔案即可用。
#include <stdio.h>
#include <string.h> // strlen()所需標頭檔案
int main()
{
char *p = "hello, I am mike!"
int len = strlen(p); // 測字串的長度
printf("len = %d\n", len);
return 0;
}
3)如果使用使用者自己定義的函式,而該函式與呼叫它的函式(即主調函式)在檔案中,且函式定義的位置在主調函式之後,則必須在呼叫此函式之前對被呼叫的函式作宣告。
所謂函式宣告,就是在函式尚在未定義的情況下,事先將該函式的有關資訊通知編譯系統,相當於告訴編譯器,函式在後面定義,以便使編譯能正常進行。
函式宣告其一般形式為( 後面的分號不能省略 ):
型別說明符 被調函式名(型別 形參,型別 形參 …);
或為:
型別說明符 被調函式名(型別,型別 …);
括號內給出了形參的型別和形參名,或只給出形參型別。這便於編譯系統進行檢錯,以
防止可能出現的錯誤。
如:
int max(int a,int b);
或寫為:
int max(int,int);
完整例子如下:
#include <stdio.h>
int max(int x, int y); // 函式的宣告,分號不能省略
// int max(int, int); // 另一種方式
int main()
{
int a = 10, b = 25, num_max = 0;
num_max = max(a, b); // 函式的呼叫
printf("num_max = %d\n", num_max);
return 0;
}
// 函式的定義
int max(int x, int y)
{
return x>y ? x : y;
}
函式定義和定義的區別:
1)定義是指對函式功能的確立,包括指定函式名、函式型別、形參及其型別、函式體等,它是一個完整的、獨立的函式單位。
2)宣告的作用則是把函式的名字、函式型別以及形參的個數、型別和順序(注意,不包括函式體)通知編譯系統,以便在對包含函式呼叫的語句進行編譯時,據此對其進行對照檢查(例如函式名是否正確,實參與形參的型別和個數是否一致)。