
目錄引言1. 函數簡介定義功能基本語法2. 函數的聲明與定義函數聲明Function Declaration函數定義Function Definition函數分離編譯3. 函數調用基本調用多次調用4. 參數傳遞機制傳值調用傳引用調用傳指針調用選擇合適的傳遞方式5. 返回值5.1 返回基本數據類型5.2 返回引用5.3 返回指針5.4 返回對象6. 函數重載定義規則示例注意事項7. 默認參數定義規則示例注意事項8. 內聯函數定義使用場景示例優點缺點注意事項9. 遞歸函數定義基本結構示例計算階乘示例斐波那契數列注意事項遞歸優化尾遞歸10. Lambda表達式定義語法組件說明高級示例捕獲并排序使用Lambda表達式與標準庫11. 函數指針與回調函數函數指針聲明與使用函數指針作為參數回調函數與Lambda表達式結合結語引言初學 C 的同學一定會長期和函數打交道它是代碼封裝、代碼復用的核心基礎但很多新手極易踩坑分不清傳值、傳引用、傳指針三者區別搞不懂函數聲明與分離編譯寫遞歸出現棧溢出不會使用 Lambda 與函數指針回調。本文完整梳理 C 函數11 大核心板塊從基礎定義、聲明調用、三種參數傳遞、各類返回值寫法到函數重載、默認參數、內聯函數、遞歸、Lambda 匿名函數、函數指針與回調全覆蓋每個知識點配套可直接復制運行的完整代碼同時標注新手高頻易錯點不管是課堂練習、期末復習還是項目開發都能直接復用。1. 函數簡介定義函數是執行特定任務的代碼塊可以被程序中的多個地方調用。使用函數可以增加代碼的可重用性、可讀性和可維護性。功能封裝將特定功能封裝在函數中便于管理和修改。復用同一段功能代碼可以在程序中多次調用減少代碼重復。結構化通過函數組織代碼提高程序的結構清晰度。基本語法return_type function_name(parameter_list) {// function body}這個return_type是指類型int、void.....示例#include iostream // 函數聲明 int add(int a, int b); int main() { int result add(5, 3); std::cout 5 3 result std::endl; return 0; } // 函數定義 int add(int a, int b) { return a b; }輸出5 3 82. 函數的聲明與定義函數聲明Function Declaration告訴編譯器函數的名稱、返回類型和參數類型但不包含函數體。通常放在頭文件中或在使用函數前進行聲明。語法return_type function_name(parameter_list);示例int multiply(int a, int b); // 函數聲明函數定義Function Definition提供函數的具體實現包括函數體。函數定義可以在源文件中或與聲明一起在頭文件中出現推薦僅聲明在頭文件中。語法return_type function_name(parameter_list) {// function body}示例int multiply(int a, int b) { // 函數定義 return a * b; }函數分離編譯聲明放在頭文件如multiply.h。定義放在源文件如multiply.cpp。multiply.h:#ifndef MULTIPLY_H #define MULTIPLY_H int multiply(int a, int b); #endifmultiply.cpp:#include multiply.h int multiply(int a, int b) { return a * b; }main.cpp:#include iostream #include multiply.h int main() { int result multiply(4, 6); std::cout 4 * 6 result std::endl; return 0; }編譯命令假設使用gg main.cpp multiply.cpp -o program輸出4 * 6 243. 函數調用基本調用通過函數名稱和必要的參數來調用函數。示例#include iostream void greet() { std::cout Hello, World! std::endl; } int main() { greet(); // 調用函數 return 0; }輸出Hello, World!多次調用同一函數可以在程序中被調用多次。示例#include iostream void sayHello() { std::cout Hello! std::endl; } int main() { sayHello(); sayHello(); sayHello(); return 0; }輸出Hello! Hello! Hello!4. 參數傳遞機制C中函數參數的傳遞方式主要有以下三種1.傳值調用Pass by Value2.傳引用調用Pass by Reference3.傳指針調用Pass by Pointer傳值調用定義函數接收參數的副本函數內對參數的修改不會影響原始數據。語法void function_name(int a) {a 10; // 只修改副本}示例#include iostream void changeValue(int num) { num 100; std::cout Inside function: num std::endl; } int main() { int value 50; changeValue(value); std::cout Outside function: value std::endl; return 0; }輸出Inside function: 100 Outside function: 50解釋changeValue函數修改的是num的副本原始變量value保持不變。傳引用調用定義函數接收參數的引用函數內對參數的修改會影響原始數據。語法void function_name(int a) {a 10; // 修改原始數據}示例#include iostream void changeValue(int num) { num 100; std::cout Inside function: num std::endl; } int main() { int value 50; changeValue(value); std::cout Outside function: value std::endl; return 0; }輸出Inside function: 100 Outside function: 100解釋changeValue函數通過引用修改了原始變量value的值。傳指針調用定義函數接收指向參數的指針函數內通過指針可以修改原始數據。語法void function_name(int *a) {*a 10; // 修改原始數據}示例#include iostream void changeValue(int *num) { *num 100; std::cout Inside function: *num std::endl; } int main() { int value 50; changeValue(value); std::cout Outside function: value std::endl; return 0; }輸出Inside function: 100 Outside function: 100解釋changeValue函數通過指針修改了原始變量value的值。選擇合適的傳遞方式傳值調用適用于不需要修改原始數據且數據量較小的情況。傳引用調用適用于需要修改原始數據或傳遞大型數據結構以提高效率。傳指針調用類似傳引用調用但更靈活可用于傳遞nullptr或指向動態分配的內存。5. 返回值函數可以通過return語句將結果返回給調用者。返回值的類型可以是基本數據類型、引用、指針、對象等。5.1 返回基本數據類型示例#include iostream int add(int a, int b) { return a b; } int main() { int sum add(3, 4); std::cout Sum: sum std::endl; return 0; }輸出Sum: 75.2 返回引用注意返回引用需要確保引用的對象在返回后依然有效不要返回局部變量。示例#include iostream // 返回引用注意不要返回局部變量 /* int getValue(){ int a 100; return a; //外部需要引用a,在花括號結束后a就被釋放了所以無效 } */ int getMax(int a, int b) { if(a b) return a; else return b; } int main() { // auto getvalue getValue(); 無效接受返回的局部變量的值 // std::cout value getvalue std::endl; int x 10; int y 20; int max getMax(x, y); std::cout Max: max std::endl; max 30; // 修改引用 std::cout After modification, y: y std::endl; return 0; }輸出Max: 20 After modification, y: 30解釋getMax函數返回較大的變量的引用修改max實際上修改了y。5.3 返回指針示例#include iostream // 返回指針注意不要返回局部變量的地址 /* int * getValueP(){ int a 10; return a; } */ // 動態開辟數組堆內存 int* allocateArray(int size){ int* arr new int[size]; //用new開辟數組首地址用delete回收 for(int i 0; i size; i){ arr[i] i*2; } return arr; //在堆上生命周期很長可以返回局部變量 } int main() { // 在堆上創建數組 int size 5; int* myArray allocateArray(size); for(int i 0; i size; i) std::cout myArray[i] ; std::cout std::endl; delete[] myArray; // 釋放內存回收連續數組的空間 return 0; }輸出0 2 4 6 8解釋allocateArray函數動態分配一個數組并返回指向數組的指針。調用者需負責釋放內存。5.4 返回對象示例#include iostream #include string class Person { public: std::string name; int age; Person(std::string n, int a) : name(n), age(a) {} }; Person createPerson(std::string name, int age) { Person p(name, age); return p; // 返回對象 } int main() { Person person createPerson(Alice, 25); std::cout Name: person.name , Age: person.age std::endl; return 0; }輸出Name: Alice, Age: 25解釋createPerson函數返回一個Person對象。現代編譯器通過返回值優化減少對象拷貝提高效率。6. 函數重載定義函數重載允許在同一個作用域內定義多個名稱相同但參數列表不同的函數。編譯器通過參數列表的不同來區分調用哪個函數。規則函數名相同。參數列表類型、數量或順序不同。返回類型不參與重載的區分。示例#include iostream // 重載函數不同參數數量 int add(int a, int b) { return a b; } int add(int a, int b, int c) { return a b c; } // 重載函數不同參數類型 double add(double a, double b) { return a b; } int main() { std::cout add(2, 3) add(2, 3) std::endl; std::cout add(2, 3, 4) add(2, 3, 4) std::endl; std::cout add(2.5, 3.5) add(2.5, 3.5) std::endl; return 0; }輸出add(2, 3) 5 add(2, 3, 4) 9 add(2.5, 3.5) 6注意事項僅返回類型不同的重載是非法的。默認參數可能會與重載產生沖突使用時需謹慎。非法示例double add(int a, int b) { return a b 0.0; } // 沖突重載僅返回類型不同 // 會導致編譯錯誤 // double add(int a, int b);7. 默認參數定義函數參數可以指定默認值調用函數時可以省略這些參數默認值將被使用。規則默認參數從右到左設置不能部分設置。函數聲明和定義中默認參數只需在聲明中指定在函數的定義中直接寫就行了。示例#include iostream // 函數聲明時指定默認參數 void displayInfo(std::string name, int age 18, std::string city Unknown); int main() { displayInfo(Bob, 25, New York); // 全部參數傳遞 displayInfo(Charlie, 30); // 省略city displayInfo(Diana); // 省略age和city return 0; } // 函數定義 void displayInfo(std::string name, int age, std::string city) { std::cout Name: name , Age: age , City: city std::endl; }輸出Name: Bob, Age: 25, City: New York Name: Charlie, Age: 30, City: Unknown Name: Diana, Age: 18, City: Unknown注意事項默認參數都要放到后面不能跳過中間參數。如果同時使用默認參數和重載可能會產生歧義需謹慎設計。8. 內聯函數定義內聯函數通過在函數前加inline關鍵字建議編譯器將函數代碼嵌入到調用處減少函數調用的開銷。使用場景適用于函數體積小、調用頻繁的函數如訪問器getter和修改器setter等。示例#include iostream // 內聯函數 inline int square(int x) { return x * x; } int main() { std::cout Square of 5: square(5) std::endl; return 0; }輸出Square of 5: 25優點減少函數調用的開銷如棧操作。可能提高程序性能。缺點使得代碼體積增大可能影響緩存性能。編譯器可能忽略內聯請求特別是對于復雜函數。注意事項編譯器對inline關鍵字的處理是建議性質最終是否內聯由編譯器決定。過度使用內聯函數可能導致代碼膨脹。9. 遞歸函數定義遞歸函數是指在函數體內調用自身的函數。遞歸通常用于解決可以分解為相似子問題的問題如階乘、斐波那契數列、樹的遍歷等。基本結構遞歸函數通常包含兩個部分基準情形Base Case直接返回結果避免無限遞歸。遞歸情形Recursive Case將問題分解為更小的子問題并調用自身。示例計算階乘#include iostream // 遞歸函數計算階乘 long long factorial(int n) { if(n 0) return -1; // 錯誤情況 if(n 0 || n 1) return 1; // 基準情形 return n * factorial(n - 1); // 遞歸情形 } int main() { int number 5; long long result factorial(number); if(result ! -1) std::cout number ! result std::endl; else std::cout Invalid input! std::endl; return 0; }輸出5! 120示例斐波那契數列#include iostream // 遞歸函數計算斐波那契數 int fibonacci(int n) { if(n 0) return 0; if(n 1) return 1; return fibonacci(n - 1) fibonacci(n - 2); } int main() { int term 10; std::cout Fibonacci( term ) fibonacci(term) std::endl; return 0; }輸出Fibonacci(10) 55注意事項基準情形必須正確設置避免無限遞歸導致棧溢出。效率問題一些遞歸實現可能效率低下如斐波那契數列可以通過“記憶化”或改用迭代方法優化。堆棧深度遞歸深度過大可能導致棧溢出需避免深度遞歸。遞歸優化尾遞歸尾遞歸是指遞歸調用在函數的最后一步可以被編譯器優化為循環減少堆棧消耗。示例尾遞歸階乘#include iostream // 輔助函數用于尾遞歸 long long factorialHelper(int n, long long accumulator) { if(n 0) return accumulator; return factorialHelper(n - 1, n * accumulator); } // 尾遞歸函數 long long factorial(int n) { return factorialHelper(n, 1); } int main() { int number 5; std::cout number ! factorial(number) std::endl; return 0; }輸出5! 120解釋factorialHelper函數的遞歸調用是函數的最后一步編譯器可以將其優化為迭代減少堆棧消耗。10. Lambda表達式定義Lambda表達式是C11引入的匿名函數便于在需要函數對象的地方快速定義和使用函數。它允許定義內聯的、小型的可調用對象無需單獨定義函數。語法[ capture_list ] ( parameter_list ) - return_type {// function body}[捕獲列表](形參列表) - 返回值類型 { 函數體 };示例#include iostream #include vector #include algorithm int main() { std::vectorint numbers {1, 2, 3, 4, 5}; // 使用Lambda表達式打印每個元素 std::for_each(numbers.begin(), numbers.end(), [](int x) { std::cout x ; }); std::cout std::endl; // 使用Lambda表達式計算總和 int sum 0; std::for_each(numbers.begin(), numbers.end(), [sum](int x) { sum x; }); std::cout Sum: sum std::endl; return 0; }輸出1 2 3 4 5 Sum: 15組件說明1.捕獲列表指定如何訪問外部變量。1.1[ ]不捕獲任何外部變量。1.2[]按引用捕獲所有外部變量。1.3[]按值捕獲所有外部變量。1.4[x, y]按值捕獲x按引用捕獲y。2.參數列表類似普通函數的參數列表可以省略類型C14及以上支持自動類型推斷。3.返回類型可指定返回類型也可省略編譯器自動推斷。4.函數體Lambda的具體實現。for_each 工作原理取第一個迭代器一直到尾后元素的迭代器高級示例捕獲并排序#include iostream #include vector #include algorithm int main() { std::vectorint data {5, 2, 9, 1, 5, 6}; // 按降序排序使用Lambda表達式 std::sort(data.begin(), data.end(), [](int a, int b) - bool { return a b; }); std::cout Sorted data: ; for(auto num : data) std::cout num ; std::cout std::endl; return 0; }輸出Sorted data: 9 6 5 5 2 1使用Lambda表達式與標準庫C標準庫中的許多算法如std::for_each、std::sort、std::transform等常用Lambda表達式作為參數以實現自定義的操作。11. 函數指針與回調函數函數指針定義指向函數的指針變量保存函數的地址可以通過指針調用函數。聲明與使用#include iostream // 普通函數 void greet() { std::cout Hello from greet! std::endl; } int main() { // 定義函數指針 void (*funcPtr)() greet; // 通過指針調用函數 funcPtr(); return 0; }輸出Hello from greet!函數指針作為參數示例#include iostream // 接收函數指針作為參數 void execute(void (* pfunc)()){ // 解引用調用函數 //1. *pfunc → 根據地址取出函數本體 //2. 函數本體遇到調用符() → 隱式轉回地址執行 (*pfunc)(); // 也可以不解引用直接調用 pfunc(); } // 被調用的函數 void sayHi() { std::cout Hi! std::endl; } int main() { execute(sayHi); return 0; }輸出Hi!回調函數定義通過函數指針傳遞的函數通常用于在特定事件發生時執行自定義操作。示例基于函數指針的回調#include iostream // 回調類型定義 typedef void (*Callback)(); // 函數接收回調 void registerCallback(Callback cb) { std::cout Before callback std::endl; cb(); // 執行回調 std::cout After callback std::endl; } // 回調函數 void myCallback() { std::cout Callback executed! std::endl; } int main() { registerCallback(myCallback); return 0; }輸出Before callback Callback executed! After callback與Lambda表達式結合函數指針也可以指向Lambda表達式但僅限于不捕獲外部變量的Lambda。示例#include iostream // 回調類型定義 typedef void (*Callback)(); void executeCallback(Callback cb) { cb(); } int main() { // 不捕獲外部變量的Lambda Callback cb []() { std::cout Lambda callback! std::endl; }; executeCallback(cb); return 0; }輸出Lambda callback!注意捕獲外部變量的Lambda無法轉換為普通函數指針。結語今日有關函數知識就到這Fighting!