函數是 C 語言的核心組成部分,本質是一段完成特定功能的可重用代碼塊—— 通過封裝邏輯、接收輸入參數、返回處理結果,實現代碼模塊化、簡化編程、便於維護與複用,是大型 C 語言項目開發的基礎。
一、函數的核心概念與分類
1. 核心術語
- 函數名:函數的唯一標識,遵循 C 語言標識符規則(字母、數字、下劃線組成,首字符非數字),需見名知義(如
sum表示求和,sort表示排序)。 - 參數列表:函數接收的輸入數據,分為形參(函數定義時的參數,僅在函數內有效)和實參(調用函數時傳入的實際數據,需與形參類型、順序一致)。
- 返回值:函數執行後的輸出結果,通過
return語句返回,類型需與函數定義的返回值類型匹配(無返回值時用void聲明)。 - 函數體:包裹在
{}內的核心邏輯,包含變量定義、語句執行等代碼。
2. 函數分類(按定義主體)
- 庫函數:C 語言標準庫(如
stdio.h、math.h)或第三方庫提供的現成函數,直接包含頭文件即可調用,無需自行定義。示例:printf()(格式化輸出,需#include <stdio.h>)、sqrt()(求平方根,需#include <math.h>)、strlen()(計算字符串長度,需#include <string.h>)。 - 自定義函數:開發者根據需求自行設計的函數,靈活適配特定業務邏輯(如自定義求和、排序、數據處理函數)。
二、函數的定義與調用流程
1. 自定義函數的定義格式
c運行
// 無返回值無參數
void 函數名() {
函數體; // 執行特定邏輯,無需return
}
// 無返回值有參數
void 函數名(參數類型1 形參1, 參數類型2 形參2, ...) {
函數體;
}
// 有返回值有參數
返回值類型 函數名(參數類型1 形參1, 參數類型2 形參2, ...) {
函數體;
return 返回值; // 返回值類型需與定義一致
}
2. 函數調用的 3 種方式
- 直接調用:單獨作為語句執行,適用於無返回值函數或無需使用返回值的場景。示例:
printHello();(調用自定義的打印問候語函數)。 - 表達式調用:將函數調用作為表達式的一部分,使用其返回值參與計算。示例:
int result = sum(3, 5);(調用求和函數,返回值賦值給result)。 - 嵌套調用:在一個函數體內調用另一個函數,實現邏輯分層。示例:
int res = sum(sqrt(4), abs(-6));(嵌套調用sqrt和abs庫函數,結果作為sum的實參)。
3. 函數聲明(聲明與定義分離)
c運行
// 函數聲明(提前告知編譯器函數結構)
int sum(int, int);
int main() {
int a = 2, b = 3;
printf("和為:%d\n", sum(a, b)); // 調用函數(此時定義在後面,需聲明)
return 0;
}
// 函數定義(具體實現)
int sum(int x, int y) {
return x + y;
}
三、函數參數的傳遞方式
1. 值傳遞(默認傳遞方式)
- 原理:將實參的值拷貝給形參,形參是獨立變量,修改形參的值不會影響實參。
- 適用場景:無需修改實參,僅需使用實參的值(如求和、求最大值等)。示例:
c運行
void swap(int x, int y) {
int temp = x;
x = y;
y = temp; // 僅修改形參x、y,實參a、b不受影響
}
int main() {
int a = 1, b = 2;
swap(a, b);
printf("a=%d, b=%d\n", a, b); // 輸出:a=1, b=2(實參未交換)
return 0;
}
2. 地址傳遞(指針參數)
- 原理:將實參的內存地址傳遞給形參(指針變量),通過指針間接訪問並修改實參的內存數據,實現 “修改實參” 的需求。
- 適用場景:需要修改實參的值(如交換兩個變量、修改數組元素等)。示例(修正上面的交換功能):
c運行
void swap(int *x, int *y) {
int temp = *x;
*x = *y;
*y = temp; // 通過指針修改實參a、b的內存值
}
int main() {
int a = 1, b = 2;
swap(&a, &b); // 傳遞實參的地址
printf("a=%d, b=%d\n", a, b); // 輸出:a=2, b=1(實參已交換)
return 0;
}
注意:數組作為參數的特殊性
c運行
void modifyArray(int arr[], int len) { // 等價於 int *arr
for (int i = 0; i < len; i++) {
arr[i] *= 2; // 直接修改原數組元素
}
}
int main() {
int arr[] = {1, 2, 3};
modifyArray(arr, 3); // 傳遞數組名(首元素地址)
for (int i = 0; i < 3; i++) {
printf("%d ", arr[i]); // 輸出:2 4 6(原數組已修改)
}
return 0;
}
四、函數的高級應用
1. 遞歸函數(函數調用自身)
- 原理:將複雜問題拆解為與原問題結構一致的子問題,通過遞歸調用逐步簡化,直到觸發 “遞歸終止條件”(避免無限遞歸)。
- 適用場景:階乘計算、斐波那契數列、樹 / 圖遍歷等。示例(計算 n 的階乘):
c運行
int factorial(int n) {
if (n == 1) return 1; // 遞歸終止條件
return n * factorial(n - 1); // 遞歸調用自身,拆解問題
}
int main() {
printf("5的階乘:%d\n", factorial(5)); // 輸出:120
return 0;
}
2. 函數指針(指向函數的指針)
- 原理:函數名是函數的入口地址,函數指針存儲該地址,可通過指針調用函數,實現 “函數回調”(如排序算法中自定義比較規則)。示例:
c運行
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 函數指針:指向“返回值為int、參數為兩個int”的函數
int (*funcPtr)(int, int);
int main() {
funcPtr = add; // 指針指向add函數
printf("3+5=%d\n", funcPtr(3, 5)); // 輸出:8(通過指針調用add)
funcPtr = sub; // 指針指向sub函數
printf("7-2=%d\n", funcPtr(7, 2)); // 輸出:5(通過指針調用sub)
return 0;
}
3. 多文件編程(函數的分文件組織)
sum.h(頭文件,聲明函數):int sum(int a, int b);sum.c(源文件,定義函數):#include "sum.h" int sum(int a, int b) { return a + b; }main.c(主文件,調用函數):#include "sum.h" #include <stdio.h> int main() { printf("%d\n", sum(2,3)); return 0; }
五、常見注意事項