一、函数的参数:“函数的输入变量”
函数的参数分为形参(形式参数)和实参(实际参数),本质都是变量,核心是 “调用函数时的参数传递”。
1. 形参 vs 实参
- 形参:函数定义时括号内声明的变量,属于函数的局部变量,仅在函数内有效;
- 实参:调用函数时传入的具体值 / 变量,会将值复制给形参(默认是 “传值调用”)。
示例 1:基础传值调用(形参修改不影响实参)
#include <stdio.h>
// 定义函数:形参a、b是局部变量
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
printf("函数内:a=%d,b=%d\n", a, b); // 输出:函数内:a=5,b=3
}
int main() {
// 实参x、y
int x = 3, y = 5;
swap(x, y); // 调用函数,将x=3、y=5复制给形参a、b
printf("函数外:x=%d,y=%d\n", x, y); // 输出:函数外:x=3,y=5(实参未变)
return 0;
}
关键说明:
- 传值调用时,形参是实参的 “副本”,函数内修改形参不会影响外部实参;
- 若想修改实参,需用指针传参(新手必学的进阶用法)。
示例 2:指针传参(修改实参)
#include <stdio.h>
// 形参是指针变量(存储实参的地址)
void swap(int *a, int *b) {
int temp = *a; // *a表示取指针a指向的变量值(即x)
*a = *b; // 修改指针a指向的变量值(即修改x)
*b = temp; // 修改指针b指向的变量值(即修改y)
printf("函数内:*a=%d,*b=%d\n", *a, *b); // 输出:*a=5,*b=3
}
int main() {
int x = 3, y = 5;
swap(&x, &y); // 传入x、y的地址(&是取地址符)
printf("函数外:x=%d,y=%d\n", x, y); // 输出:x=5,y=3(实参被修改)
return 0;
}
二、函数内的局部变量:“函数专属的临时变量”
函数内定义的变量(包括形参)都是局部变量,核心特性:
- 作用域:仅限定义它的函数 / 代码块(如
if/for块)内,外部无法访问; - 生命周期:函数调用时分配内存,函数执行结束后立即释放(值丢失);
- 就近原则:若局部变量与全局变量同名,函数内优先使用局部变量。
示例:局部变量的特性
#include <stdio.h>
// 全局变量
int num = 100;
void test_local() {
// 局部变量(与全局变量同名)
int num = 200;
// 局部代码块变量(仅if内有效)
if (1) {
int temp = 300;
printf("if块内temp:%d\n", temp); // 输出:300
printf("函数内num(局部):%d\n", num); // 输出:200(就近原则)
}
// printf("if块外temp:%d\n", temp); // 报错:temp未定义(作用域仅限if块)
}
int main() {
test_local();
printf("函数外num(全局):%d\n", num); // 输出:100(全局变量未被修改)
return 0;
}
三、函数中变量的存储类别:“控制变量的生命周期”
C 语言可通过存储类别关键字控制函数内变量的存储方式,核心有 4 种,重点掌握static(新手易混淆):
| 存储类别 | 关键字 | 存储位置 | 生命周期 | 作用域 | 适用场景 |
|---|---|---|---|---|---|
| 自动变量 | auto | 栈内存 | 函数调用→结束(临时) | 函数 / 代码块内 | 普通局部变量(默认) |
| 静态变量 | static | 全局存储区 | 程序启动→结束(永久) | 函数 / 代码块内 | 需保留值的局部变量(如计数器) |
| 寄存器变量 | register | CPU 寄存器 | 函数调用→结束 | 函数 / 代码块内 | 高频访问的变量(如循环变量) |
| 外部变量 | extern | 全局存储区 | 程序启动→结束 | 整个程序 | 引用其他文件 / 全局变量 |
核心示例:static静态局部变量(最常用)
#include <stdio.h>
void count() {
// 静态局部变量:仅初始化1次,函数结束后值保留
static int cnt = 0;
cnt++;
printf("调用次数:%d\n", cnt);
}
int main() {
count(); // 输出:调用次数:1
count(); // 输出:调用次数:2
count(); // 输出:调用次数:3
return 0;
}
对比普通局部变量:若去掉static,每次调用cnt都会被初始化为 0,输出永远是 1。
示例:register寄存器变量(优化访问速度)
#include <stdio.h>
void loop() {
// 寄存器变量:优先存CPU寄存器,访问比栈内存快
register int i;
for (i = 0; i < 1000000; i++) {
// 高频循环,用register提升效率
}
printf("循环结束,i=%d\n", i);
}
int main() {
loop();
return 0;
}
四、函数返回值与变量:“避坑要点”
函数用return返回变量时,需注意变量的生命周期,否则会导致程序崩溃 / 结果错误:
- ✅ 允许返回:局部变量的值(如
return a;); - ❌ 禁止返回:局部变量的地址(如
return &a;)—— 函数结束后局部变量内存释放,地址失效。
错误示例(返回局部变量地址)
#include <stdio.h>
int* bad_return() {
int a = 10;
return &a; // 错误:返回局部变量的地址
}
int main() {
int *p = bad_return();
printf("*p = %d\n", *p); // 结果随机(内存已释放)
return 0;
}
正确示例(返回局部变量值)
#include <stdio.h>
int good_return() {
int a = 10;
return a; // 正确:返回值的副本
}
int main() {
int val = good_return();
printf("val = %d\n", val); // 输出:10
return 0;
}
总结
- 函数参数:默认是传值调用(形参不影响实参),修改实参需用指针传参;
- 局部变量:函数内默认是
auto类型,生命周期随函数结束而销毁,static可让值永久保留; - 返回值避坑:可返回局部变量的值,但绝对不能返回局部变量的地址(内存失效)。