一、概述
C 程序是由函數(shù)組成的,我們寫的代碼都是由主函數(shù) main()開始執(zhí)行的。函數(shù)是 C 程序的基本模塊,是用于完成特定任務的程序代碼單元。
1.1 函數(shù)分類
從函數(shù)定義的角度看,函數(shù)可分為系統(tǒng)函數(shù)和用戶定義函數(shù)兩種:
- 系統(tǒng)函數(shù),即庫函數(shù):這是由編譯系統(tǒng)提供的,用戶不必自己定義這些函數(shù),可以直接使用它們,如我們常用的打印函數(shù)printf()。
- 用戶定義函數(shù):用以解決用戶的專門需要。
1.2 函數(shù)作用
- 函數(shù)的使用可以省去重復代碼的編寫,降低代碼重復率
- 函數(shù)可以讓程序更加模塊化,從而有利于程序的閱讀,修改和完善
二、函數(shù)的定義
函數(shù)定義的一般形式:
返回數(shù)據(jù)的數(shù)據(jù)類型 函數(shù)名(形式參數(shù)列表)
{
數(shù)據(jù)定義部分;
執(zhí)行語句部分;
}
函數(shù)說明

三、函數(shù)相關說明
- 函數(shù)名:理論上是可以隨意起名字,最好起的名字見名知意,應該讓用戶看到這個函數(shù)名字就知道這個函數(shù)的功能。注意,函數(shù)名的后面有個圓換號(),代表這個為函數(shù),不是普通的變量名。
- 形參列表: 在定義函數(shù)時指定的形參,在未出現(xiàn)函數(shù)調用時,它們并不占內存中的存儲單元,因此稱它們是形式參數(shù)或虛擬參數(shù),簡稱形參,表示它們并不是實際存在的數(shù)據(jù),所以,形參里的變量不能賦值
- 在定義函數(shù)時指定的形參,必須是,
類型+變量的形式; - 在定義函數(shù)時指定的形參,可有可無,根據(jù)函數(shù)的需要來設計,如果沒有形參,圓括號內容為空,或寫一個
void關鍵字
-
函數(shù)體: 花括號
{ }里的內容即為函數(shù)體的內容,這里為函數(shù)功能實現(xiàn)的過程,這和以前的寫代碼沒太大區(qū)別,以前我們把代碼寫在main()函數(shù)里,現(xiàn)在只是把這些寫到別的函數(shù)里。 - 返回值: 函數(shù)的返回值是通過函數(shù)中的return語句獲得的,return后面的值也可以是一個表達式。
- 盡量保證return語句中表達式的值和函數(shù)返回類型是同一類型。
- 如果函數(shù)返回的類型和return語句中表達式的值不一致,則以函數(shù)返回類型為準,即函數(shù)返回類型決定返回值的類型。對數(shù)值型數(shù)據(jù),可以自動進行類型轉換。
- 如果函數(shù)返回的類型和return語句中表達式的值不一致,而它又無法自動進行類型轉換,程序則會報錯。
- return語句的另一個作用為中斷return所在的執(zhí)行函數(shù),類似于break中斷循環(huán)、switch語句一樣。
- 如果函數(shù)帶返回值,return后面必須跟著一個值,如果函數(shù)沒有返回值,函數(shù)名字的前面必須寫一個
void關鍵字,這時候,我們寫代碼時也可以通過return中斷函數(shù)(也可以不用)。
四、函數(shù)的調用
定義函數(shù)后,我們需要調用此函數(shù)才能執(zhí)行到這個函數(shù)里的代碼段。這和main()函數(shù)不一樣,main()為編譯器設定好自動調用的主函數(shù),無需人為調用,我們都是在main()函數(shù)里調用別的函數(shù),一個 C 程序里有且只有一個main()函數(shù)。
4.1 函數(shù)執(zhí)行流程
#include <stdio.h>
int main() {
void print(); // 函數(shù)聲明
char *s = "hello world"; // 實參
print(s); // 調用函數(shù);s為形參
return 0;
}
void print(char *s) // 函數(shù)定義
{
printf("%s\n", s); // 函數(shù)執(zhí)行體
}
- 首先執(zhí)行
main()函數(shù) - 執(zhí)行
print()函數(shù)
4.2 函數(shù)的形參和實參
- 形參出現(xiàn)在函數(shù)定義中,在整個函數(shù)體內都可以使用,離開該函數(shù)則不能使用。
- 實參出現(xiàn)在主調函數(shù)中,進入被調函數(shù)后,實參也不能使用。
- 實參變量對形參變量的數(shù)據(jù)傳遞是“值傳遞”,即單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。
- 在調用函數(shù)時,編譯系統(tǒng)臨時給形參分配存儲單元。調用結束后,形參單元被釋放。
- 實參單元與形參單元是不同的單元。調用結束后,形參單元被釋放,函數(shù)調用結束返回主調函數(shù)后則不能再使用該形參變量。實參單元仍保留并維持原值。因此,在執(zhí)行一個被調用函數(shù)時,形參的值如果發(fā)生改變,并不會改變主調函數(shù)中實參的值。
4.3 函數(shù)的無參數(shù)調用
如果是調用無參函數(shù),則不能加上“實參”,但括號不能省略。
// 函數(shù)的定義
void test()
{
}
int main()
{
// 函數(shù)的調用
test(); // right, 圓括號()不能省略
test(250); // error, 函數(shù)定義時沒有參數(shù)
return 0;
}
4.4 有參函數(shù)調用
- 如果實參表列包含多個實參,則各參數(shù)間用逗號隔開。
// 函數(shù)的定義
void test(int a, int b)
{
}
int main()
{
int p = 10, q = 20;
test(p, q); // 函數(shù)的調用
return 0;
}
-
實參與形參的個數(shù)應相等,類型應匹配(相同或賦值兼容)。實參與形參按順序對應,一對一地傳遞數(shù)據(jù)。
- 實參可以是常量、變量或表達式,無論實參是何種類型的量,在進行函數(shù)調用時,它們都必須具有確定的值,以便把這些值傳送給形參。所以,這里的變量是在圓括號( )外面定義好、賦好值的變量。
// 函數(shù)的定義
void test(int a, int b)
{
}
int main()
{
// 函數(shù)的調用
int p = 10, q = 20;
test(p, q); // right
test(11, 30 - 10); // right
test(int a, int b); // error, 不應該在圓括號里定義變量
return 0;
}
五、函數(shù)返回值
- 如果函數(shù)定義沒有返回值,函數(shù)調用時不能寫void關鍵字,調用函數(shù)時也不能接收函數(shù)的返回值。
// 函數(shù)的定義
void test()
{
}
int main()
{
// 函數(shù)的調用
test(); // right
void test(); // error, void關鍵字只能出現(xiàn)在定義,不可能出現(xiàn)在調用的地方
int a = test(); // error, 函數(shù)定義根本就沒有返回值
return 0;
}
- 如果函數(shù)定義有返回值,這個返回值我們根據(jù)用戶需要可用可不用,但是,假如我們需要使用這個函數(shù)返回值,我們需要定義一個匹配類型的變量來接收。
// 函數(shù)的定義, 返回值為int類型
int test()
{
}
int main()
{
// 函數(shù)的調用
int a = test(); // right, a為int類型
int b;
b = test(); // right, 和上面等級
char *p = test(); // 雖然調用成功沒有意義, p為char *, 函數(shù)返回值為int, 類型不匹配
// error, 必須定義一個匹配類型的變量來接收返回值
// int只是類型,沒有定義變量
int = test();
// error, 必須定義一個匹配類型的變量來接收返回值
// int只是類型,沒有定義變量
int test();
return 0;
}
六、函數(shù)聲明
如果使用用戶自己定義的函數(shù),而該函數(shù)與調用它的函數(shù)(即主調函數(shù))不在同一文件中,或者函數(shù)定義的位置在主調函數(shù)之后,則必須在調用此函數(shù)之前對被調用的函數(shù)作聲明。
所謂函數(shù)聲明,就是在函數(shù)尚在未定義的情況下,事先將該函數(shù)的有關信息通知編譯系統(tǒng),相當于告訴編譯器,函數(shù)在后面定義,以便使編譯能正常進行。
注意:一個函數(shù)只能被定義一次,但可以聲明多次。
#include <stdio.h>
int max(int x, int y); // 函數(shù)的聲明,分號不能省略
// int max(int, int); // 另一種方式
int main()
{
int a = 10, b = 25, num_max = 0;
num_max = max(a, b); // 函數(shù)的調用
printf("num_max = %d\n", num_max);
return 0;
}
// 函數(shù)的定義
int max(int x, int y)
{
return x > y ? x : y;
}
函數(shù)定義和聲明的區(qū)別:
- 定義是指對函數(shù)功能的確立,包括指定函數(shù)名、函數(shù)類型、形參及其類型、函數(shù)體等,它是一個完整的、獨立的函數(shù)單位。
- 聲明的作用則是把函數(shù)的名字、函數(shù)類型以及形參的個數(shù)、類型和順序(注意,不包括函數(shù)體)通知編譯系統(tǒng),以便在對包含函數(shù)調用的語句進行編譯時,據(jù)此對其進行對照檢查(例如函數(shù)名是否正確,實參與形參的類型和個數(shù)是否一致)。
七、main函數(shù)與exit函數(shù)
在main函數(shù)中調用exit和return結果是一樣的,但在子函數(shù)中調用return只是代表子函數(shù)終止了,在子函數(shù)中調用exit,那么程序終止。
#include <stdio.h>
#include <stdlib.h>
void fun()
{
printf("fun\n");
//return;
exit(0);
}
int main()
{
fun();
while (1);
return 0;
}
八、多文件(分文件)編程
- 把函數(shù)聲明放在頭文件xxx.h中,在主函數(shù)中包含相應頭文件
- 在頭文件對應的xxx.c中實現(xiàn)xxx.h聲明的函數(shù)
防止頭文件重復包含
當一個項目比較大時,往往都是分文件,這時候有可能不小心把同一個頭文件 include 多次,或者頭文件嵌套包含。
為了避免同一個文件被include多次,C/C++中有兩種方式,一種是 #ifndef 方式,一種是 #pragma once 方式。
- 方法一、
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
// 聲明語句
#endif
- 方法二
#pragma once
// 聲明語句
