1.函数的概念
1. 按函数来源分类
- 库函数:由C标准库提供的预定义函数,无需用户手动编写,只需要包含对应头文件即可直接调用
- 输入输出类:
printf、scanf、puts,需要包含头文件<stdio.h> - 字符串处理类:
strlen、strcpy、strcmp,需要包含头文件<string.h> - 数学计算类:
sqrt、sin、fabs,需要包含头文件<math.h> - 内存管理类:
malloc、free,需要包含头文件<stdlib.h>
- 输入输出类:
- 用户自定义函数:程序员根据业务需求自行编写的函数,是C语言程序中最灵活的部分
2. 按函数形式分类
- 无参函数:不需要接收外部参数,执行固定逻辑,比如打印固定提示信息的
print_welcome() - 有参函数:调用时需要传入实际参数,函数根据参数执行对应逻辑,比如计算两个数和的
add(int a, int b) - 有返回值函数:执行完成后会向主调函数返回一个结果,返回值类型需要在函数定义时声明
- 无返回值函数:执行完成后不返回结果,此时函数的返回类型必须声明为
void,比如只负责打印日志的log_info(char* msg) 2.函数的使用
#include <stdio.h> int Add(int x, int y) { int z = 0; z = x+y; return z; } int main() { int a = 0; int b = 0; //输⼊ scanf("%d %d", &a, &b); int r = Add(a, b); //输出 printf("%d\n", r); return 0; }3.形参和实参
4.return语句
return语句的语法非常简洁,根据是否需要返回值分为两种合法写法:
- 无表达式形式:直接写
return;,仅用于终止当前函数执行,不返回任何数据。这种写法只能用在返回值类型为void的函数中。 - 带表达式形式:写为
return 表达式;,执行时会先计算表达式的值,再将这个值返回给主调函数。C语言语法不强制要求给表达式加括号,return a + b;和return (a + b);是完全等价的,后者只是部分程序员的书写习惯。
注意:如果在非void返回类型的函数中省略return后面的表达式,函数的返回值会处于未定义状态,编译器会直接抛出警告,属于典型的代码隐患。
5.嵌套调⽤和链式访问
嵌套调用
int year(int y) { if(((y%4==0)&&(y%100!=0))||(y%400==0)) return 1; else return 0; } int days_of_month(int y, int m) { int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int day = days[m]; if (year(y) && m == 2) day += 1; return day; } int main() { int y = 0; int m = 0; scanf("%d %d", &y, &m); int d = days_of_month(y, m); printf("%d\n", d); return 0; }链式访问
核心规则非常简单:把一个函数的返回值直接作为另一个函数的入参,不需要额外定义中间变量存储结果。
- 典型示例:我们平时计算字符串长度时,
printf("%d", strlen("abcdef"))就是最基础的链式访问,strlen的返回值直接传给printf作为打印参数,省去了定义临时变量int len的步骤。 - 经典易错案例:很多初学者会遇到的
printf("%d", printf("%d", printf("%d", 23))),运行结果是2321。这是因为printf函数的返回值是本次打印输出的字符个数:最内层先打印23(2个字符,返回2),中间层打印返回值2(1个字符,返回1),最外层打印返回值1,最终连起来输出2321。
6.函数的声明和定义
#include <stido.h> int is_leap_year(int y);//函数声明 int main() { int y = 0; scanf("%d", &y); int r = is_leap_year(y); if(r == 1) printf("闰年\n"); else printf("⾮闰年\n"); return 0; } int is_leap_year(int y) { if(((y%4==0)&&(y%100!=0)) || (y%400==0)) return 1; else return 0; }如果面对多个文件时,我们可以把函数的声明、类型的声明放在头⽂件(.h)中,函数的实现是放在源⽂件(.c)⽂件中。
7.static和extern
| 对比维度 | static | extern |
|---|---|---|
| 核心作用 | 限定作用域、延长局部变量生命周期 | 声明外部已定义的实体,扩展跨文件访问能力 |
| 内存分配 | 修饰变量时会在静态数据区分配内存,是定义的一部分 | 仅做声明,不会分配任何内存,不创建实体 |
| 链接属性 | 将全局变量/函数改为内部链接,仅当前文件可见 | 用于引用外部链接的实体,打通跨文件访问通道 |
| 初始化规则 | 未显式赋值时自动初始化为0,局部静态变量仅初始化一次 | 完全不涉及初始化逻辑,初始化由变量的定义处完成 |
| 重复定义兼容性 | 不同文件中定义同名static变量不会冲突,各自独立 | 全局变量只能在整个工程中定义一次,多文件定义会触发链接错误 |