C 的本质 函数接口

本文详细解析了函数中的传入参数、传出参数及Value-result参数的概念,并介绍了如何使用两层指针来实现灵活的数据传递。此外,还探讨了回调函数和可变参数的应用场景。
  • 传入参数与传出参数

    如果函数接口有指针参数,既可以把指针所指向的数据传给函数使用(称为传入参数),也可以由函数填充指针所指的内存空间,传回给调用者使用(称为传出参数),例如strcpysrc参数是传入参数,dest参数是传出参数。有些函数的指针参数同时担当了这两种角色,如select(2)fd_set *参数,既是传入参数又是传出参数,这称为Value-result参数
1.传入参数示例:void func(const unit_t *p);
C语言本质鈥斺07--函数接口

2.传出参数示例:void func(unit_t *p);
C语言本质鈥斺07--函数接口

3.Value-result参数示例:void func(unit_t *p);
C语言本质鈥斺07--函数接口
    传出参数举例:
C语言本质鈥斺07--函数接口

  • 两层指针的参数
    两层指针也是指针,同样可以表示传入参数、传出参数或者Value-result参数,只不过该参数所指的内存空间应该解释成一个指针变量

#ifndef REDIRECT_PTR_H
#define REDIRECT_PTR_H
extern void get_a_day(const char **);
#endif



#include "redirect_ptr.h"

static const char *msg[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
            "Thursday", "Friday", "Saturday"};
void get_a_day(const char **pp)
{
     static int i = 0;
     *pp = msg[i%7];
     i++;
}




#include <stdio.h>
#include "redirect_ptr.h"

int main(void)
{
     const char *firstday = NULL;
     const char *secondday = NULL;
     get_a_day(&firstday);
     get_a_day(&secondday);
     printf("%s\t%s\n", firstday, secondday);
     ret
urn 0;
}

    两层指针作为传出参数还有一种特别的用法,可以在函数中分配内存,调用者通过传出参数取得指向该内存的指针
    通过参数分配内存示例:void alloc_unit(unit_t **pp); void free_unit(unit_t *p);
C语言本质鈥斺07--函数接口
C语言本质鈥斺07--函数接口

C语言本质鈥斺07--函数接口

C语言本质鈥斺07--函数接口


  • 返回值是指针
    如果返回值传出的是指针,分为两种情况:第一种是传出指向静态内存或已分配的动态内存的指针,例如localtime(3)inet_ntoa(3),第二种是在函数中动态分配内存并传出指向这块内存的指针,例如malloc(3),这种情况通常还要实现一个释放内存的函数。其实质就相当于一个指针函数。
1.返回指向已分配内存的指针示例:unit_t *func(void);
C语言本质鈥斺07--函数接口
C语言本质鈥斺07--函数接口

2.动态分配内存并返回指针示例:unit_t *alloc_unit(void);
                            void free_unit(unit_t *p);

C语言本质鈥斺07--函数接口

C语言本质鈥斺07--函数接口
C语言本质鈥斺07--函数接口



  • 回调函数

    如果参数是一个函数指针,调用者可以传递一个函数的地址给实现者,让实现者去调用它,这称为回调函数(Callback Function) 实质即是函数指针
       回调函数示例:void func(void (*f)(void *), void *p);
C语言本质鈥斺07--函数接口

例1:实现了一个repeat_three_times函数,可以把调用者传来的任何回调函数连续执行三次

#ifndef PARA_CALLBACK_H
#define PARA_CALLBACK_H

typedef void (*callback_t)(void *);
extern void repeat_three_times(callback_t, void *);

#endif


#include "para_callback.h"

void repeat_three_times(callback_t f, void *para)
{
     f(para);
     f(para);
     f(para);
}


#include <stdio.h>
#include "para_callback.h"

void say_hello(void *str)
{
     printf("Hello %s\n", (const char *)str);
}

void count_numbers(void *num)
{
     int i;
     for(i=1; i<=(int)num; i++)
      printf("%d ", i);
     putchar('\n');
}

int main(void)
{
     repeat_three_times(say_hello, "Guys");
     repeat_three_times(count_numbers, (void *)4);
     return 0;
}


  • 可变参数

   只见过一个带有可变参数的函数printf
                        int printf(const char *format, ...);
C语言本质鈥斺07--函数接口

       确定可变参数的个数,在参数列表的末尾传一个Sentinel,例如NULL。下面实现一个printlist函数,可以打印若干个传入的字符串。
C语言本质鈥斺07--函数接口







<think>我们正在讨论函数接口,需要结合用户提供的引用内容。用户引用了三个来源: 引用[1]提到了接口函数和调用函数的区别,其中接口函数需要重定义方法才能被调用,而调用函数是已经实现的。 引用[2]定义了函数接口:有且只有一个抽象方法的接口,可以有默认方法和静态方法,通常用@FunctionalInterface注解标记。 引用[3]说明了函数接口作为参数的使用,可以用lambda表达式或方法引用来实现。 用户的问题:想了解函数接口(即函数接口)的相关信息,包括概念、使用方法、与接口的区别等。 因此,我们的回答需要涵盖: 1. 函数接口的概念(从引用[2]中提取核心定义)。 2. 函数接口的使用方法(包括定义、实现、调用,特别是lambda表达式和方法引用,参考引用[3])。 3. 与普通接口的区别(参考引用[1]和引用[2],普通接口可以有多个抽象方法,而函数接口只能有一个)。 另外,注意用户之前的问题是关于IT领域接口的,我们已经详细介绍了接口,现在需要聚焦在函数接口上。 根据系统指令,我们需要注意数学公式的格式(使用$...$和$$...$$),代码块使用正确,引用标识自然添加。 结构安排: 1. 概念:明确函数接口的定义。 2. 使用方法:如何定义、实现(lambda和方法引用)和调用。 3. 与普通接口的区别:重点在抽象方法的数量限制。 4. 总结并生成相关问题。 注意:用户引用的内容中,引用[1]虽然主要讲的是C语言中接口函数和调用函数的区别,但这里我们讨论的是Java等语言中的函数接口,所以需要适当关联。引用[1]中提到的“接口函数要重定义方法才能被调用”类似于函数接口需要被实现(通过lambda或方法引用)才能调用。 下面开始组织回答。 </think> ### 函数接口详解 #### 1. 函数接口的概念 函数接口是**有且仅有一个抽象方法**的接口(可包含多个默认方法或静态方法)。其核心价值在于支持函数式编程范式,允许通过Lambda表达式或方法引用简洁地实现接口[^2]。关键特性包括: - **抽象方法唯一性**:必须只包含一个未实现的抽象方法,如`Runnable`接口的`run()`方法 - **注解标记**:通常用`@FunctionalInterface`注解声明(非强制,但可帮助编译器验证) - **函数式签名**:方法签名决定了Lambda表达式的参数和返回值类型匹配规则[^3] 数学表达示例: 若定义函数接口$F$,其抽象方法为$f: X \rightarrow Y$,则Lambda表达式需满足: $$ \lambda\ x \in X \ . \ \text{expr}(x) \in Y $$ #### 2. 函数接口的使用方法 ##### (1) 定义接口 ```java @FunctionalInterface interface MathOperation { int calculate(int a, int b); // 唯一抽象方法 default void log() { /* 默认方法 */ } // 允许默认方法 } ``` ##### (2) 实现方式 - **Lambda表达式**(最常用): ```java MathOperation add = (x, y) -> x + y; // 实现calculate方法 System.out.println(add.calculate(3, 5)); // 输出8 ``` - **方法引用**: ```java MathOperation max = Math::max; // 等价于 (a,b) -> Math.max(a,b) ``` ##### (3) 作为参数传递 ```java public void execute(MathOperation op, int x, int y) { System.out.println(op.calculate(x, y)); } // 调用 execute((a, b) -> a * b, 4, 5); // 输出20 [^3] ``` #### 3. 与普通接口的区别 | **特性** | 函数接口 | 普通接口 | |------------------|-------------------------------|-----------------------------| | 抽象方法数量 | **严格1个** | 任意数量 | | 主要用途 | Lambda表达式/方法引用的目标 | 定义通用行为契约 | | 实现方式 | Lambda自动实现 | 需显式编写实现类 | | 注解支持 | 推荐@FunctionalInterface | 无特定注解 | | 兼容性 | 可包含default/static方法[^2] | 同左 | #### 关键区别解析 - **抽象方法约束**:普通接口如`Comparator`可定义多个抽象方法(如`compare`和`equals`),但函数接口必须满足$\|\text{抽象方法}\| = 1$[^2] - **实现逻辑**:函数接口的实例本质是**行为的具体实现**(如`(a,b)->a+b`),而普通接口需通过类完整实现所有抽象方法[^1] - **设计目标**:函数接口优化了行为传递(如事件处理),普通接口侧重对象能力描述 #### 总结 函数接口通过单抽象方法约束,为Lambda表达式提供类型安全的实现载体,显著简化了行为参数化编程。其核心优势在于: 1. 代码简洁性:用Lambda替代匿名内部类 2. 行为抽象化:将函数作为一等公民传递 3. 兼容性:保留接口的默认方法特性 当需要将**具体行为**作为参数传递时(如集合操作`list.forEach()`),函数接口是最佳选择;而需要定义**多方法契约**时(如DAO接口),应使用普通接口
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值