C语言--函数变量2

0 阅读5分钟

一、函数的参数:“函数的输入变量”

函数的参数分为形参(形式参数)和实参(实际参数),本质都是变量,核心是 “调用函数时的参数传递”。

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;
}

二、函数内的局部变量:“函数专属的临时变量”

函数内定义的变量(包括形参)都是局部变量,核心特性:

  1. 作用域:仅限定义它的函数 / 代码块(如if/for块)内,外部无法访问;
  2. 生命周期:函数调用时分配内存,函数执行结束后立即释放(值丢失);
  3. 就近原则:若局部变量与全局变量同名,函数内优先使用局部变量。

示例:局部变量的特性

#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全局存储区程序启动→结束(永久)函数 / 代码块内需保留值的局部变量(如计数器)
寄存器变量registerCPU 寄存器函数调用→结束函数 / 代码块内高频访问的变量(如循环变量)
外部变量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;
}

总结

  1. 函数参数:默认是传值调用(形参不影响实参),修改实参需用指针传参;
  2. 局部变量:函数内默认是auto类型,生命周期随函数结束而销毁,static可让值永久保留;
  3. 返回值避坑:可返回局部变量的值,但绝对不能返回局部变量的地址(内存失效)。