C语言函数——模块化编程的利器(附完整代码示例) 函数是C语言的“积木”让复杂任务分解为小块提升代码复用性和可读性。本篇博客从库函数到自定义函数从形参实参到static和extern带你全面掌握C语言函数的核心知识并附上优化后的实践代码。 目录函数的概念库函数与头文件自定义函数形参与实参return语句详解数组作为函数参数嵌套调用与链式访问函数的声明与定义static和extern关键字综合实战日期天数计算器总结1. 函数的概念函数function也叫子程序是一段完成特定任务的代码块。C语言程序就是由多个函数组合而成的函数可以复用提高开发效率。函数的好处分解复杂问题模块化设计避免重复代码减少冗余便于团队协作和后期维护在C语言中函数分为两类库函数C标准库提供的现成函数如printf、scanf自定义函数我们自己编写的函数2. 库函数与头文件2.1 什么是库函数C语言标准ANSI C规定了常用函数的标准称为标准库。编译器厂商根据标准实现这些函数供程序员直接使用。例如printf、scanf、strlen、sqrt等。优点现成的无需重复造轮子质量高效率有保证2.2 头文件库函数声明在对应的头文件中使用前必须包含。例如stdio.h输入输出函数math.h数学函数string.h字符串处理函数stdlib.h标准库函数如rand、srandtime.h时间函数2.3 库函数使用示例sqrtc#include stdio.h #include math.h // 必须包含 int main() { double x 16.0; double result sqrt(x); printf(sqrt(%.2f) %.2f\n, x, result); return 0; }输出textsqrt(16.00) 4.003. 自定义函数3.1 函数语法c返回类型 函数名(形式参数列表) { // 函数体 return 返回值; }返回类型函数计算结果的类型可以是void无返回值函数名见名知意如Add、isLeapYear参数传递给函数的数据可为空void函数体具体的执行语句3.2 示例加法函数优化版c#include stdio.h // 加法函数返回x和y的和 int Add(int x, int y) { return x y; // 直接返回表达式 } int main() { int a, b; printf(请输入两个整数); scanf(%d %d, a, b); int sum Add(a, b); printf(%d %d %d\n, a, b, sum); return 0; }优化说明函数体简洁直接返回表达式参数名清晰易于理解4. 形参与实参4.1 概念区分实际参数实参调用函数时传入的具体值如Add(a, b)中的a和b。形式参数形参函数定义中的占位符如Add(int x, int y)中的x和y。4.2 关系与内存形参在函数调用时才分配内存接收实参的值。形参是实参的临时拷贝修改形参不影响实参除非传地址后续指针会讲。调试验证在VS中查看地址可发现形参和实参地址不同证明是独立空间。c#include stdio.h void test(int x) { printf(形参 x 的地址%p\n, x); } int main() { int a 10; printf(实参 a 的地址%p\n, a); test(a); return 0; }5. return语句详解return用于结束函数并返回值注意事项返回值可以是表达式return x y;void函数可省略return或写return;用于提前结束return执行后函数立即结束后续代码不执行。返回类型自动转换若返回类型与声明不一致会隐式转换可能警告。所有分支必须有return否则编译报错。不写返回类型默认int不推荐要显式写出。c#include stdio.h // 正确所有分支有return int max(int a, int b) { if (a b) return a; else return b; } // void函数提前返回 void printPositive(int n) { if (n 0) return; // 提前结束 printf(%d\n, n); }6. 数组作为函数参数6.1 关键规则数组作为实参时传递的是首元素地址形参接收的是指针但可以用数组形式书写。形参数组大小可以省略如int arr[]因为实际大小需额外传递。形参和实参操作的是同一数组修改形参数组会改变原数组。6.2 示例设置数组为-1并打印c#include stdio.h // 将数组所有元素设为 -1 void setArray(int arr[], int size) { for (int i 0; i size; i) { arr[i] -1; } } // 打印数组 void printArray(int arr[], int size) { for (int i 0; i size; i) { printf(%d , arr[i]); } printf(\n); } int main() { int arr[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int sz sizeof(arr) / sizeof(arr[0]); printf(原始数组); printArray(arr, sz); setArray(arr, sz); printf(置为-1后); printArray(arr, sz); return 0; }输出text原始数组1 2 3 4 5 6 7 8 9 10 置为-1后-1 -1 -1 -1 -1 -1 -1 -1 -1 -17. 嵌套调用与链式访问7.1 嵌套调用函数可以相互调用形成嵌套。例如计算某年某月有多少天c#include stdio.h #include stdbool.h // 判断闰年 bool isLeapYear(int year) { return (year % 4 0 year % 100 ! 0) || (year % 400 0); } // 获取某月天数 int getDaysOfMonth(int year, int month) { // 每月天数下标0不用 int days[] {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int day days[month]; if (isLeapYear(year) month 2) { day; // 闰年2月多一天 } return day; } int main() { int y, m; printf(请输入年份和月份如 2025 6); scanf(%d %d, y, m); printf(%d年%d月有%d天\n, y, m, getDaysOfMonth(y, m)); return 0; }这里getDaysOfMonth调用了isLeapYear体现了函数组合。7.2 链式访问将函数的返回值直接作为另一个函数的参数像链条一样。经典题目c#include stdio.h #include string.h int main() { // printf返回打印的字符个数 printf(%d\n, printf(%d\n, printf(Hello))); return 0; }分析最内层printf(Hello)打印5个字符返回5。第二个printf(%d\n, 5)打印5\n共2个字符返回2。最外层printf(%d\n, 2)打印2\n共2个字符。最终输出textHello 5 28. 函数的声明与定义8.1 单个文件如果函数定义在调用之后必须提前声明否则编译器会警告。声明格式返回类型 函数名(参数类型列表);参数名可省略c#include stdio.h // 函数声明 int isLeapYear(int year); // 或 int isLeapYear(int); int main() { int y 2024; if (isLeapYear(y)) printf(闰年\n); else printf(非闰年\n); return 0; } // 函数定义 int isLeapYear(int year) { return (year % 4 0 year % 100 ! 0) || (year % 400 0); }8.2 多文件组织实际项目中通常将函数声明放在头文件.h定义放在源文件.c主程序包含头文件即可。文件结构textadd.h → 声明 add.c → 定义 test.c → 主程序add.hc#ifndef __ADD_H__ #define __ADD_H__ int Add(int x, int y); #endifadd.cc#include add.h int Add(int x, int y) { return x y; }test.cc#include stdio.h #include add.h // 包含自定义头文件 int main() { int a 10, b 20; printf(%d %d %d\n, a, b, Add(a, b)); return 0; }使用头文件的好处声明集中方便模块化管理。9. static和extern关键字9.1 作用域与生命周期作用域变量/函数可见的范围。生命周期变量从创建到销毁的时间。局部变量作用域在函数内生命周期随函数调用开始和结束。全局变量作用域整个工程生命周期贯穿程序运行。9.2 static修饰局部变量效果延长生命周期但作用域不变。变量存储在静态区程序结束才销毁。c#include stdio.h void count() { static int c 0; // 只初始化一次下次进入保留值 c; printf(%d , c); } int main() { for (int i 0; i 5; i) { count(); } return 0; } // 输出1 2 3 4 5使用场景需要函数内保留上次状态的变量如计数器。9.3 static修饰全局变量效果将全局变量的外部链接属性改为内部链接只能在本文件使用其他文件即使extern也无法访问。add.ccstatic int g_val 100; // 仅本文件可见test.c无法访问cextern int g_val; // 链接错误使用场景全局变量只在本文件内使用防止被外部误修改。9.4 static修饰函数与修饰全局变量类似将函数的外部链接改为内部链接仅本文件可见。add.ccstatic int Add(int x, int y) { return x y; }test.c无法调用cextern int Add(int, int); // 链接错误使用场景辅助函数仅供内部使用不暴露给外部。9.5 extern声明外部符号用于声明其他文件中定义的全局变量或函数以便在当前文件使用。add.ccint g_val 2025;test.cc#include stdio.h extern int g_val; // 声明外部变量 int main() { printf(%d\n, g_val); return 0; }10. 综合实战日期天数计算器结合所学编写一个程序输入年份和月份输出该月天数输入年份和某日计算该日是这一年的第几天。优化实现多文件结构date_utils.hc#pragma once #include stdbool.h bool isLeapYear(int year); int getDaysOfMonth(int year, int month); int getDayOfYear(int year, int month, int day);date_utils.cc#include date_utils.h bool isLeapYear(int year) { return (year % 4 0 year % 100 ! 0) || (year % 400 0); } int getDaysOfMonth(int year, int month) { static const int days[] {0,31,28,31,30,31,30,31,31,30,31,30,31}; if (month 2 isLeapYear(year)) return 29; return days[month]; } int getDayOfYear(int year, int month, int day) { int sum 0; for (int m 1; m month; m) { sum getDaysOfMonth(year, m); } return sum day; }test.cc#include stdio.h #include date_utils.h int main() { int year, month, day; printf(请输入年份和月份如 2025 6); scanf(%d %d, year, month); printf(%d年%d月有%d天\n, year, month, getDaysOfMonth(year, month)); printf(请输入日期年 月 日); scanf(%d %d %d, year, month, day); printf(这是%d年的第%d天\n, year, getDayOfYear(year, month, day)); return 0; }11. 总结概念要点函数定义返回类型 函数名(参数) { 体 }库函数使用前需包含对应头文件形参与实参实参传递给形参形参是副本return结束函数并返回值void可省略数组传参传递首地址形参操作原数组嵌套调用函数之间相互调用链式访问返回值作为另一个函数的参数声明与定义先声明后使用.h放声明.c放定义static修饰局部变量生命周期延长作用域不变static修饰全局变量/函数作用域限制在本文件外部不可见extern声明外部符号变量/函数核心思想函数是模块化编程的基础合理使用函数能让代码清晰、可维护、可复用。多练习、多拆分功能你一定能写出优雅的C语言程序 所有代码均在VS2022和GCC下测试通过。如有疑问欢迎在评论区交流下一篇预告C语言指针——内存操作的基石