从零开始的敲代码生活--C语言篇(函数)
2026/8/19 13:20:22 网站建设 项目流程

C语言函数完整整理

一、函数核心作用

  1. 模块化拆分代码,减少重复冗余代码,提高复用性;
  2. 降低代码耦合,逻辑分层清晰,便于阅读、修改、调试;
  3. 实现功能封装,对外只暴露接口,隐藏内部实现细节

二、函数完整语法

1. 函数定义(实现)

返回值类型 函数名(形参列表) { 函数体(功能代码); return 返回值; // void函数可省略return }
各部分说明
  1. 返回值类型
    • int/char/float/double/结构体/指针:函数运算结束返回对应数据;
    • void:无返回值,函数仅执行操作,无需return;
    • 补充:非void函数必须写return,否则返回随机垃圾值
  2. 函数名 遵循标识符规则:字母、数字、下划线,不能数字开头;推荐动宾命名get_max() print_arr(),见名知意
  3. 形参列表 格式:类型1 变量1, 类型2 变量2; 无参数写void(标准规范,fun(void)),空括号fun()老式写法不推荐
  4. 函数体 大括号不可省略,内部是实现逻辑;
  5. return
    • return 数值/变量;:终止当前函数,把值返回给调用处;
    • return;:仅结束void函数,无返回数据

2. 函数调用

格式:函数名(实参列表);

int max(int a,int b){ return a>b?a:b; } int main(){ int res = max(10,20); // 接收返回值 max(5,8); // 不接收返回值,直接丢弃结果 return 0; }
调用规则
  1. 实参与形参个数、顺序、数据类型必须一一匹配;
  2. 实参可以是常量、变量、表达式;
  3. 函数不调用则不执行,调用一次执行一次

3. 函数声明

作用

若函数定义写在main之后,编译器识别不到函数,报隐式声明警告,需提前声明

语法(声明只写头,无函数体)
返回值类型 函数名(形参类型列表); // 示例 int max(int a, int b); void print_str(char str[]);
使用场景
  1. 单个.c文件内,函数定义在主函数下方;
  2. 多文件工程:函数写在其他.c,在头文件.h统一声明,外部文件#include即可调用。

三、函数调用底层流程

  1. 保护现场:调用时将下一条指令地址、局部寄存器压入栈;
  2. 分配形参栈空间,将实参拷贝给形参(值传递);
  3. 执行函数内部代码;
  4. 恢复现场:函数结束,弹出栈中保存的地址,回到调用位置继续执行;
  5. 释放函数栈内所有局部变量,局部变量生命周期结束。

补充:函数栈空间有限,递归过深会栈溢出程序崩溃

四、函数传参三大核心方式

1. 值传递(普通变量传参)

规则:实参拷贝一份副本给形参,形参修改不影响外部实参

void change(int n){ n = 100; // 仅修改副本,main里num不变 } int main(){ int num = 10; change(num); printf("%d",num); // 输出10 }

适用场景:仅读取数据,不需要修改外部变量

2. 地址传递(一级指针传参)

实参传递变量地址,形参用指针接收,通过*修改原变量内存,外部实参同步变化

void change(int *n){ *n = 100; // 直接修改main中num的内存 } int main(){ int num = 10; change(&num); printf("%d",num); // 输出100 }

适用场景:

  1. 函数内需要修改外部普通变量;
  2. 函数返回多个结果;
  3. 传递数组(数组名本质首地址)

3. 二级指针传参

实参传递指针变量的地址,形参类型**接收,可在函数内修改外部指针本身的指向

void set_str(char **p){ *p = "hello c"; // 修改外部指针pstr的指向 } int main(){ char *pstr = "test"; set_str(&pstr); }

典型用途:操作指针数组、动态分配内存返回堆地址

五、数组作为函数参数

1. 一维整形数组传参

两种等价形参写法,数组形参会退化指针,函数内sizeof(数组名)仅得8字节指针大小,必须手动传长度:

// 写法1 void print(int arr[], int len); // 写法2(推荐,直观体现指针本质) void print(int *arr, int len);

2. 一维字符数组(字符串)传参

字符串自带\0结束符,无需传长度:

int mystrlen(char str[]); int mystrlen(char *str);

3. 二维数组传参

行数可省略,列绝对不能省略,二维数组名是数组指针int (*)[列]

void show(int arr[][4], int row); void show(int (*arr)[4], int row);

二维字符数组同理:

void print_strs(char str[][100], int line);

六、递归函数

定义

函数直接/间接调用自身,用递归代替循环完成重复逻辑

必备条件

  1. 递归出口(终止条件):必须存在结束分支,否则无限递归栈溢出;
  2. 每次递归向出口靠近

优缺点

  • 优点:代码简洁,树、阶乘、斐波那契等场景逻辑直观;
  • 缺点:频繁栈保护/恢复,运行效率低于循环,深度过大会栈溢出
// 递归求阶乘 int fact(int n){ if(n == 1) return 1; // 递归出口 return n * fact(n-1); }

七、特殊函数拓展(笔记补充)

1. 指针函数(返回指针的函数)

返回值为类型*,禁止返回局部栈变量地址(函数结束栈销毁,野指针)。 合法返回值:全局变量、static静态变量、字符串常量、堆malloc地址、传入的参数地址

char *get_str(){ static char buf[20] = "hello"; // static生命周期全局,合法 return buf; }

2. 函数指针(指向函数的指针)

存储函数入口地址,多用于回调函数解耦代码。 语法:返回值(*指针名)(形参列表)

int add(int a,int b){return a+b;} int (*pfun)(int,int) = add; // 定义并赋值 int res = p(3,5); // 通过指针调用函数

3. main主函数

程序入口,系统自动调用,标准规范写法:

int main(void){ return 0; // 0代表程序正常退出 }

八、变量作用域&函数搭配补充

  1. 局部变量(函数内/形参) 作用域仅限当前{},栈存储,函数结束自动销毁,默认随机值;
  2. static局部静态变量 生命周期全局(程序运行全程),仅第一次调用初始化,保留上次值;
  3. 全局变量 所有函数均可访问,数据区存储,默认初始0,多文件用extern声明

九、高频易错点

  1. 函数声明和定义返回值、形参类型必须完全一致,否则编译报错;
  2. 值传递无法修改外部变量,改值必须传地址;
  3. 数组作参数退化为指针,不能用sizeof求数组长度;
  4. 递归必须写出口,否则栈溢出崩溃;
  5. 指针函数不能返回局部栈数组/变量地址;
  6. void函数内return后不能加数值;
  7. 二维数组传参,列数不可省略;
  8. 函数实参求值顺序自右向左

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询