ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

万字详解 C++ 函数:声明定义、传参、返回值、重载、回调全套可运行代码

万字详解 C++ 函数:声明定义、传参、返回值、重载、回调全套可运行代码 目录引言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!
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表