【C语言入门】 “无参函数” 与 “有参函数”

0. 前置知识:什么是函数?

在 C 语言中,函数是一段完成特定功能的独立代码块,可以被重复调用。它的核心作用是 “封装逻辑,复用代码”。例如,你可能写一个函数来计算两个数的和,或者写一个函数来打印一段文字。

函数的本质是 “解决问题的步骤”:把复杂的任务拆分成小步骤,每个步骤用一个函数实现,代码会更清晰、更易维护。

1. 无参函数的定义与特点
1.1 无参函数的标准格式

无参函数的定义形式为:

返回值类型 函数名() {
    // 函数体(具体执行的代码)
}

  • 返回值类型:函数执行后返回结果的类型(如intvoid等)。如果不需要返回结果,用void声明。
  • 函数名:给函数起的名字(符合 C 语言命名规则,如print_hello)。
  • 括号 ():表示 “无参数”(括号内为空,或显式写void,如void func(void),两者等价)。
1.2 无参函数的典型场景

无参函数适用于功能固定、不需要外部输入的场景。常见例子包括:

  • 初始化操作:比如初始化硬件(如串口、LED 灯)、初始化全局变量。

    void init_led() {
        // 代码:配置LED引脚为输出模式
        GPIO_InitTypeDef GPIO_InitStruct = {0};
        GPIO_InitStruct.Pin = GPIO_PIN_5;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    }
    
     

    这段代码是单片机开发中常见的 LED 初始化函数,每次调用都会执行固定的引脚配置逻辑,不需要额外参数。

  • 固定输出功能:比如打印一段提示语、生成固定格式的日志。

    void print_welcome() {
        printf("********************************\n");
        printf("*    欢迎使用学生管理系统!    *\n");
        printf("********************************\n");
    }
    
     

    每次调用print_welcome(),都会输出相同的欢迎界面,不需要传入任何数据。

  • 循环执行的固定任务:比如在嵌入式系统中,周期性读取传感器(传感器地址固定)。

    void read_temp_sensor() {
        // 代码:读取固定地址的温度传感器数据
        float temp = I2C_Read(0x48);  // 0x48是传感器的固定I2C地址
        printf("当前温度:%.1f℃\n", temp);
    }
    
1.3 无参函数的优缺点
  • 优点:逻辑简单、调用方便。不需要考虑参数传递的复杂度,适合功能高度固定的场景。
  • 缺点:灵活性差。如果需求稍有变化(比如打印不同的欢迎语),就需要修改函数本身,无法通过外部输入调整行为。
2. 有参函数的定义与核心概念
2.1 有参函数的标准格式

有参函数的定义形式为:

返回值类型 函数名(参数类型1 参数名1, 参数类型2 参数名2, ...) {
    // 函数体(使用参数完成逻辑)
}

  • 参数列表:括号内是 “形式参数”(简称 “形参”),表示函数需要的输入数据类型和名称。
  • 形参的作用:在函数内部,形参是变量,用于接收调用函数时传入的 “实际参数”(简称 “实参”)。
2.2 关键概念:形参 vs 实参
  • 形参(形式参数):函数定义时声明的参数,相当于 “占位符”。它只有在函数被调用时才会分配内存,函数执行结束后内存释放。
    例如:

    int add(int a, int b) {  // a和b是形参
        return a + b;
    }
    
  • 实参(实际参数):调用函数时传入的具体数据。实参可以是常量、变量或表达式,但必须与形参的类型和数量匹配。
    例如:

    int x = 3, y = 5;
    int sum = add(x, y);  // x和y是实参(也可以直接写add(3,5))
    
2.3 有参函数的参数传递方式

在 C 语言中,参数传递有两种方式:值传递地址传递(指针传递)。

2.3.1 值传递(最常用)
  • 规则:将实参的值复制一份传给形参。函数内部对形参的修改不会影响实参。
  • 示例
    void swap(int a, int b) {  // 形参a、b是实参的“副本”
        int temp = a;
        a = b;
        b = temp;  // 仅交换形参的值,实参不受影响
    }
    
    int main() {
        int x = 10, y = 20;
        swap(x, y);  // 调用swap函数
        printf("x=%d, y=%d\n", x, y);  // 输出:x=10, y=20(未交换)
        return 0;
    }
    

    为什么没交换?因为swap函数中的abxy的 “复制值”,修改它们不会改变原始的xy
2.3.2 地址传递(用于修改外部变量)
  • 规则:将实参的地址(指针)传给形参。函数内部通过指针直接操作实参的内存,修改会影响实参。
  • 示例
    void swap(int *a, int *b) {  // 形参是指针,接收实参的地址
        int temp = *a;
        *a = *b;  // 通过指针修改实参的内存
        *b = temp;
    }
    
    int main() {
        int x = 10, y = 20;
        swap(&x, &y);  // 传入x和y的地址(实参是指针)
        printf("x=%d, y=%d\n", x, y);  // 输出:x=20, y=10(交换成功)
        return 0;
    }
    

    这里swap函数的形参是指针(int *a),通过&x&y获取实参的地址,直接操作原始内存,因此能真正交换xy的值。
2.4 有参函数的典型场景

有参函数适用于功能依赖外部输入的场景。常见例子包括:

2.4.1 数学计算(需输入数据)

比如计算两个数的乘积、求数组的平均值等:

// 计算两个整数的乘积(值传递)
int multiply(int a, int b) {
    return a * b;
}

// 计算数组的平均值(地址传递,避免复制大数组)
float average(int *arr, int length) {  // arr是数组的地址,length是数组长度
    int sum = 0;
    for (int i = 0; i < length; i++) {
        sum += arr[i];
    }
    return (float)sum / length;
}
2.4.2 条件判断(需输入判断依据)

比如判断一个数是否为质数、判断字符串是否相等:

// 判断n是否为质数(n是输入参数)
int is_prime(int n) {
    if (n <= 1) return 0;  // 小于等于1不是质数
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return 0;  // 能被整除,不是质数
    }
    return 1;  // 是质数
}
2.4.3 硬件控制(需输入配置参数)

比如控制 LED 灯的亮度(通过 PWM 占空比)、设置串口的波特率:

// 配置串口波特率(USART是串口硬件地址,baud是波特率参数)
void uart_init(USART_TypeDef *USART, int baud) {
    USART->BRR = SystemCoreClock / baud;  // 根据baud计算波特率寄存器值
    USART->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;  // 使能串口收发
}
2.5 有参函数的优缺点
  • 优点:灵活性强。通过参数调整,同一个函数可以实现多种功能(比如average函数可以计算任意数组的平均值)。
  • 缺点:复杂度高。需要考虑参数的类型匹配、传递方式(值 / 地址)、边界条件(如数组长度是否越界)等问题。
3. 无参函数与有参函数的对比
对比维度无参函数有参函数
输入依赖无(功能固定)有(功能依赖参数)
灵活性低(修改功能需改函数本身)高(通过参数调整功能)
复用性低(仅适用于固定场景)高(适用于多种场景)
参数传递复杂度无(无需处理参数)高(需处理类型、传递方式等)
4. 实际开发中的选择策略
4.1 何时用无参函数?
  • 功能高度固定,且未来不会变化(如打印固定日志)。
  • 不需要外部数据输入(如初始化硬件)。
  • 简化代码调用(避免传递参数的开销)。
4.2 何时用有参函数?
  • 功能需要根据不同输入调整(如计算不同数的和)。
  • 需要复用函数处理多组数据(如计算多个数组的平均值)。
  • 需要与外部数据交互(如读取不同地址的传感器)。
5. 常见误区与注意事项
5.1 无参函数的 “空参数” 声明

在 C 语言中,无参函数的参数列表可以写void(显式声明无参),也可以留空。但留空不代表 “任意参数”,而是 “参数类型不确定”(这是 C 语言的历史遗留问题)。为了避免歧义,建议显式写void

// 推荐写法(显式声明无参)
void print_hello(void) {
    printf("Hello, world!\n");
}

// 不推荐写法(可能被误解为“参数类型不确定”)
void print_hello() {
    printf("Hello, world!\n");
}
5.2 有参函数的参数类型匹配

实参和形参的类型必须严格匹配,否则会导致错误或未定义行为。例如:

// 函数声明需要int类型参数
void print_number(int n) {
    printf("Number: %d\n", n);
}

int main() {
    float x = 3.14;
    print_number(x);  // 警告:将float传给int会截断小数(3.14→3)
    return 0;
}
5.3 地址传递的风险

使用指针传递参数时,若函数内部修改了指针指向的内存,可能会影响外部变量。需确保操作的合法性(如指针不为空、数组不越界):

// 错误示例:修改空指针指向的内存(崩溃!)
void set_value(int *p, int value) {
    *p = value;  // 如果p是NULL,会导致段错误
}

int main() {
    int *ptr = NULL;
    set_value(ptr, 10);  // 危险!ptr未指向有效内存
    return 0;
}

6. 总结:如何快速区分无参函数与有参函数?
  • 看函数定义的括号:无参函数括号内是空或void;有参函数括号内有参数类型和名称。
  • 看功能是否依赖外部输入:无参函数 “自己搞定一切”;有参函数 “需要你给信息才能做事”。

用生活类比,形象理解 “无参函数” 与 “有参函数”

为了让你快速记住这两个概念,我们先抛开代码,用生活中的场景打个比方:

1. 无参函数:像自动售货机的 “取货按钮”

假设你家楼下有一台自动售货机,里面只卖一种矿泉水(比如 “固定款”)。你只需要按一下 “取货按钮”(触发函数),机器就会 “哐当” 掉出一瓶水。这个过程不需要你输入任何额外信息(比如 “要冰的”“要大瓶的”)—— 按钮的功能是固定的、无输入要求的

这就是无参函数:函数执行时不需要 “外部输入数据”,它的功能是提前设定好的,无论何时调用,行为都一致。

2. 有参函数:像咖啡店的 “点单系统”

再想象你走进一家咖啡店,想点一杯咖啡。店员会问你:“要美式还是拿铁?要冰的还是热的?糖要加多少?”(这些问题就是在 “要参数”)。你回答了这些问题(输入参数),店员才会根据你的要求制作咖啡(函数根据参数执行逻辑)。

这就是有参函数:函数执行时需要 “外部输入数据”(参数),不同的参数会让函数产生不同的行为或结果。

一句话总结
  • 无参函数:“我不需要你给我东西,我自己知道该做什么”(功能固定,无输入)。
  • 有参函数:“你得告诉我具体要求,我才能帮你做事”(功能灵活,依赖输入)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值