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