完整且详细的C语言教程,从入门到精通(5.1)--C语言函数
你们好我是一名嵌入式工程师。我正在持续更新《嵌入式工程师的C语言实战》系列教程目前已更新6篇。如果你也在学习嵌入式欢迎点个关注我们一起从零到一。文章目录前言一、什么是函数函数的作用二、函数的定义语法示例各部分详解三、函数的声明原型语法示例声明与定义的区别四、函数的调用调用方式示例五、函数的参数传递1. 值传递基本类型2. 指针传递模拟引用传递3. 数组作为参数六、函数的返回值1. 返回基本类型2. 返回指针3. 返回结构体注意4. void 函数无返回值七、函数与变量的作用域1. 局部变量2. 全局变量3. static 局部变量八、递归函数示例计算阶乘递归的注意点九、inline 函数十、函数指针进阶总结函数的要点速记完整综合示例如果这篇文章对你有帮助欢迎点赞、收藏、关注主播是一个马上粉丝量超1个的嵌入式内容主播。谢谢前言函数是C语言程序的基本构建块它让你能够将代码组织成可重用的模块。下面系统地讲解C语言函数的各个方面。一、什么是函数函数是一段封装了特定功能的代码块可以通过函数名被调用执行。它能够接收输入参数执行操作并返回一个结果返回值。函数的作用代码复用避免重复编写相同的代码。模块化将复杂程序分解为小的、可管理的部分。可读性通过有意义的函数名提高代码清晰度。可维护性修改某个功能只需修改对应的函数。二、函数的定义函数定义包括返回类型、函数名、参数列表和函数体。语法返回类型 函数名(参数类型1参数名1,参数类型2参数名2,...){// 函数体// 执行操作return返回值;// 如果返回类型不是 void必须 return}示例#includestdio.h// 定义一个函数计算两个整数的和intadd(inta,intb){intresultab;returnresult;}intmain(){intsumadd(3,5);// 调用函数printf(3 5 %d\n,sum);return0;}各部分详解部分说明示例返回类型函数返回值的类型若没有返回值则用voidint,float,void函数名标识符用于调用函数遵循命名规则add,print_message参数列表接收的输入由类型和名称组成可以为空int a, int b或void函数体花括号{}内的代码包含具体逻辑return a b;return语句结束函数执行并返回一个值void函数可以没有return 0;三、函数的声明原型函数声明也称为函数原型告诉编译器函数的存在及其签名但不提供具体实现。声明通常放在头文件中或在调用函数之前。语法返回类型 函数名(参数类型列表);示例#includestdio.h// 函数声明原型intadd(inta,intb);voidprint_hello(void);intmain(){intsumadd(10,20);// 可以在声明之后调用print_hello();return0;}// 函数定义intadd(inta,intb){returnab;}voidprint_hello(void){printf(Hello, World!\n);}声明与定义的区别对比项函数声明函数定义是否有函数体没有以分号结尾有花括号括起来的代码是否分配内存不分配分配函数代码空间位置头文件或源文件开头源文件中数量限制可以多次声明只能定义一次四、函数的调用调用方式函数名(实际参数列表);示例#includestdio.hintmax(inta,intb){return(ab)?a:b;}intmain(){intx10,y20;// 直接调用并赋值给变量intmaximummax(x,y);printf(较大值: %d\n,maximum);// 函数调用作为表达式的一部分printf(最大值是: %d\n,max(100,50));// 忽略返回值如果函数有返回值但不需要max(7,3);// 合法返回值被丢弃return0;}五、函数的参数传递C语言中参数传递是值传递pass by value——函数接收的是实参的副本修改形参不会影响实参。1. 值传递基本类型#includestdio.hvoidincrement(intn){nn1;// 修改的是形参的副本printf(函数内部: n %d\n,n);}intmain(){intx5;increment(x);printf(主函数中: x %d\n,x);// x 仍然是 5return0;}2. 指针传递模拟引用传递通过传递指针可以在函数内部修改实参的值。#includestdio.hvoidswap(int*a,int*b){inttemp*a;*a*b;*btemp;}intmain(){intx10,y20;printf(交换前: x%d, y%d\n,x,y);swap(x,y);// 传递地址printf(交换后: x%d, y%d\n,x,y);return0;}3. 数组作为参数当数组作为参数传递时实际上传递的是指向第一个元素的指针数组大小信息会丢失。#includestdio.h// 方式1直接接收指针voidprint_array(intarr[],intsize){for(inti0;isize;i){printf(%d ,arr[i]);}printf(\n);}// 方式2指针语法等价voidprint_array2(int*arr,intsize){for(inti0;isize;i){printf(%d ,*(arri));}printf(\n);}intmain(){intnumbers[]{1,2,3,4,5};intlensizeof(numbers)/sizeof(numbers[0]);print_array(numbers,len);print_array2(numbers,len);return0;}六、函数的返回值1. 返回基本类型intadd(inta,intb){returnab;}2. 返回指针#includestdio.hint*find_max(int*arr,intsize){int*max_ptrarr;for(inti1;isize;i){if(arr[i]*max_ptr){max_ptrarr[i];}}returnmax_ptr;// 返回指向数组元素的指针}intmain(){intdata[]{3,7,2,9,5};int*maxfind_max(data,5);printf(最大值: %d\n,*max);return0;}3. 返回结构体注意可以返回结构体但如果结构体很大复制开销较大通常改用指针。#includestdio.htypedefstruct{intx;inty;}Point;Pointcreate_point(intx,inty){Point p;p.xx;p.yy;returnp;// 返回结构体的副本}intmain(){Point pcreate_point(3,5);printf((%d, %d)\n,p.x,p.y);return0;}4.void函数无返回值voidgreet(constchar*name){printf(Hello, %s!\n,name);// 可以不写 return或写 return; 不带值}七、函数与变量的作用域1. 局部变量在函数内部定义的变量只在函数内可见。voidfunc(){intlocal10;// 局部变量只在 func 内可见printf(%d\n,local);}// local 在这里不可见2. 全局变量在所有函数外部定义在整个文件甚至其他文件若用extern中可见。#includestdio.hintglobal_counter0;// 全局变量voidincrement(){global_counter;// 可以在函数中访问}intmain(){increment();increment();printf(global_counter %d\n,global_counter);// 输出 2return0;}3.static局部变量在函数内用static修饰的变量其值在函数调用之间保持不变。#includestdio.hvoidcount_calls(){staticintcalls0;// 只在第一次进入时初始化calls;printf(函数被调用了 %d 次\n,calls);}intmain(){count_calls();count_calls();count_calls();return0;}八、递归函数函数可以调用自身这称为递归。示例计算阶乘#includestdio.h// 递归计算 n!intfactorial(intn){if(n1){return1;// 基准情况停止条件}returnn*factorial(n-1);// 递归调用}intmain(){printf(5! %d\n,factorial(5));// 输出 120return0;}递归的注意点必须有停止条件基准情况否则会无限递归导致栈溢出。每次递归调用都会在栈上创建新的帧深度过大可能导致栈溢出。尾递归优化某些编译器支持可以避免栈增长。九、inline函数用inline关键字建议编译器将函数代码展开在调用处避免函数调用开销。适合短小、频繁调用的函数。#includestdio.hstaticinlineintsquare(intx){returnx*x;}intmain(){intresultsquare(5);// 可能被展开为 int result 5 * 5;printf(%d\n,result);return0;}十、函数指针进阶函数指针是指向函数的指针可以作为参数传递实现回调机制。#includestdio.h// 接收函数指针作为参数intcalculate(inta,intb,int(*operation)(int,int)){returnoperation(a,b);}intadd(inta,intb){returnab;}intsub(inta,intb){returna-b;}intmul(inta,intb){returna*b;}intmain(){intx10,y5;printf(10 5 %d\n,calculate(x,y,add));printf(10 - 5 %d\n,calculate(x,y,sub));printf(10 * 5 %d\n,calculate(x,y,mul));return0;}总结函数的要点速记要点说明定义返回类型 函数名(参数列表) { 函数体 }声明返回类型 函数名(参数类型列表);返回值用return语句返回void表示无返回值参数传递值传递复制副本用指针可修改原值作用域局部变量在函数内可见全局变量在整个文件可见递归函数调用自身必须要有停止条件函数指针可指向函数用于回调或动态调用完整综合示例#includestdio.h#includestring.h// 函数声明voidprint_header(constchar*title);intmax_of_three(inta,intb,intc);voidreverse_string(char*str);int*find_max(intarr[],intsize);intmain(){// 调用 void 函数print_header(程序开始);// 调用有返回值的函数intmaxmax_of_three(10,25,18);printf(最大值: %d\n,max);// 数组操作intnumbers[]{4,7,2,9,1};int*max_ptrfind_max(numbers,5);printf(数组最大值: %d\n,*max_ptr);// 字符串操作chartext[]Hello World;reverse_string(text);printf(反转后: %s\n,text);print_header(程序结束);return0;}// 函数定义voidprint_header(constchar*title){printf( %s \n,title);}intmax_of_three(inta,intb,intc){intmaxa;if(bmax)maxb;if(cmax)maxc;returnmax;}voidreverse_string(char*str){intlenstrlen(str);for(inti0;ilen/2;i){chartempstr[i];str[i]str[len-1-i];str[len-1-i]temp;}}int*find_max(intarr[],intsize){int*maxarr[0];for(inti1;isize;i){if(arr[i]*max){maxarr[i];}}returnmax;}如果这篇文章对你有帮助欢迎点赞、收藏、关注主播是一个马上粉丝量超1个的嵌入式内容主播。谢谢