C语言仿函数(Functor)实现示例

 更新时间:2026年01月29日 08:50:34   作者:bkspiderx  
本文档介绍了在C语言中实现仿函数(Functor)的方法,仿函数是一种带状态的可调用对象,通过结构体和函数指针的组合模拟C++中的仿函数特性,下面就来具体介绍一下如何使用

文档版本:V1.0
适用场景:C语言开发中需“带状态可调用对象”的场景(如自定义排序、计数器、带配置的回调等)
前置知识:C语言结构体、函数指针、基本内存操作

1. 概述

1.1 仿函数的本质

仿函数(Functor)起源于C++,是重载operator()的类/对象,具备两大核心特性:

  • 可调用性:像函数一样被调用(如func());
  • 状态持有性:可通过类的成员变量存储自身状态(如计数、配置参数)。

1.2 C语言模拟的必要性

C语言没有原生“类”和“运算符重载”,但实际开发中常需“带状态的回调/函数”(如“按自定义规则排序”“带步长的计数器”)。此时需通过结构体+函数指针模拟仿函数,实现“可调用+持状态”的核心能力。

1.3 C语言仿函数的定义

在C语言中,仿函数被定义为:
包含“状态变量”和“函数指针”的结构体——状态变量存储数据,函数指针指向具体调用逻辑,调用时通过函数指针访问状态并执行逻辑。

2. 核心实现原理

C语言模拟仿函数的核心是“拆分+组合”:用结构体管理状态,用函数指针定义行为,二者结合实现仿函数特性。

2.1 第一步:用结构体存储“状态”

结构体的成员变量用于保存仿函数需要维护的数据(如计数初始值、排序方向、配置参数等),示例:

// 示例:计数器的状态结构体
typedef struct {
    int count;   // 状态1:当前计数值
    int step;    // 状态2:计数步长(可选,扩展用)
} CounterState;

2.2 第二步:用函数指针定义“可调用逻辑”

函数指针指向具体的“调用函数”,该函数需满足两个要求:

  • 第一个参数必须是结构体指针(通过指针访问结构体内部状态);
  • 返回值和剩余参数根据业务场景定义(如计数器返回当前值,比较器返回比较结果)。

示例函数指针定义:

// 计数器的调用函数指针:接收结构体指针,返回当前计数值
typedef int (*CounterCallFunc)(struct Counter* self);

2.3 第三步:组合“状态+逻辑”为仿函数结构体

将“状态结构体”与“函数指针”封装为一个新结构体,形成完整的仿函数类型:

// 完整的计数器仿函数结构体
typedef struct Counter {
    // 状态部分:复用上面的状态结构体(或直接定义成员)
    int count;
    int step;
    // 逻辑部分:函数指针(指向具体调用逻辑)
    CounterCallFunc call;
} Counter;

2.4 第四步:初始化仿函数

提供初始化函数,完成“状态赋值”和“函数指针绑定”,确保仿函数创建后可直接使用:

// 初始化计数器仿函数:参数为初始值和步长
Counter Counter_Create(int initial, int step) {
    Counter cnt;
    // 初始化状态
    cnt.count = initial;
    cnt.step = step;
    // 绑定调用逻辑(关联具体的函数)
    cnt.call = Counter_Increment;
    return cnt;
}

3. 实战示例

以下两个示例覆盖“基础状态计数”和“带状态回调”两大核心场景,代码可直接编译运行。

3.1 示例1:带步长的计数器仿函数

功能需求

  • 初始化时设置“初始值”和“步长”;
  • 每次调用仿函数,按步长自增并返回当前值;
  • 支持动态修改步长(修改状态)。

完整代码

#include <stdio.h>

// 1. 定义计数器仿函数结构体(状态+逻辑)
typedef struct Counter {
    // 状态:计数值、步长
    int count;
    int step;
    // 逻辑:调用函数指针(接收自身指针,返回当前值)
    int (*call)(struct Counter* self);
} Counter;

// 2. 实现调用逻辑:按步长自增并返回当前值
int Counter_Increment(Counter* self) {
    // 通过结构体指针访问内部状态
    self->count += self->step;
    return self->count;
}

// 3. 初始化函数:创建仿函数实例
Counter Counter_Create(int initial, int step) {
    Counter cnt;
    cnt.count = initial;
    cnt.step = step;
    cnt.call = Counter_Increment; // 绑定逻辑
    return cnt;
}

// 4. 辅助函数:动态修改步长(修改状态)
void Counter_SetStep(Counter* self, int new_step) {
    self->step = new_step;
}

// 主函数测试
int main() {
    // 创建仿函数实例:初始值0,步长1
    Counter cnt = Counter_Create(0, 1);
    
    // 调用仿函数(像函数一样使用)
    printf("第1次调用:%d\n", cnt.call(&cnt)); // 输出:1
    printf("第2次调用:%d\n", cnt.call(&cnt)); // 输出:2
    
    // 修改状态(步长改为3)
    Counter_SetStep(&cnt, 3);
    printf("修改步长为3后,第3次调用:%d\n", cnt.call(&cnt)); // 输出:5
    printf("第4次调用:%d\n", cnt.call(&cnt)); // 输出:8
    
    return 0;
}

编译与运行

  • 编译命令:gcc counter_functor.c -o counter_functor
  • 运行结果:
    第1次调用:1
    第2次调用:2
    修改步长为3后,第3次调用:5
    第4次调用:8
    

3.2 示例2:带排序方向的比较器仿函数

功能需求

  • 实现自定义排序:支持“升序”和“降序”切换;
  • 排序逻辑通过仿函数封装(状态:排序方向;逻辑:比较规则);
  • 复用排序函数,仅需修改仿函数状态即可切换排序方向。

完整代码

#include <stdio.h>
#include <stdlib.h>

// 1. 定义比较器仿函数结构体(状态+逻辑)
typedef struct Comparator {
    // 状态:排序方向(1=升序,0=降序)
    int is_ascending;
    // 逻辑:比较函数指针(接收自身、两个待比较值,返回比较结果)
    int (*compare)(struct Comparator* self, int a, int b);
} Comparator;

// 2. 实现比较逻辑:根据状态返回比较结果
int Int_Compare(Comparator* self, int a, int b) {
    if (self->is_ascending) {
        return a - b; // 升序:a>b返回正数,触发交换
    } else {
        return b - a; // 降序:b>a返回正数,触发交换
    }
}

// 3. 初始化比较器仿函数
Comparator Comparator_Create(int is_ascending) {
    Comparator cmp;
    cmp.is_ascending = is_ascending;
    cmp.compare = Int_Compare;
    return cmp;
}

// 4. 通用排序函数(接收仿函数作为参数,复用逻辑)
void Sort_With_Functor(int* arr, int len, Comparator* cmp) {
    for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - i - 1; j++) {
            // 调用仿函数的比较逻辑
            if (cmp->compare(cmp, arr[j], arr[j+1]) > 0) {
                // 交换元素
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

// 辅助函数:打印数组
void Print_Array(int* arr, int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 主函数测试
int main() {
    int arr[] = {5, 2, 9, 1, 5, 6};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    printf("原始数组:");
    Print_Array(arr, len); // 输出:5 2 9 1 5 6
    
    // 1. 升序排序(使用升序比较器)
    Comparator asc_cmp = Comparator_Create(1);
    Sort_With_Functor(arr, len, &asc_cmp);
    printf("升序排序后:");
    Print_Array(arr, len); // 输出:1 2 5 5 6 9
    
    // 2. 降序排序(复用排序函数,仅修改比较器状态)
    Comparator desc_cmp = Comparator_Create(0);
    Sort_With_Functor(arr, len, &desc_cmp);
    printf("降序排序后:");
    Print_Array(arr, len); // 输出:9 6 5 5 2 1
    
    return 0;
}
编译与运行
  • 编译命令:gcc comparator_functor.c -o comparator_functor
  • 运行结果:
    原始数组:5 2 9 1 5 6 
    升序排序后:1 2 5 5 6 9 
    降序排序后:9 6 5 5 2 1 
    

4. 特点与局限

4.1 优点

  1. 灵活性高:相比单纯的函数指针,可携带状态,支持动态调整逻辑(如示例2切换排序方向);
  2. 复用性强:核心逻辑(如排序函数)可复用,仅需替换仿函数实例即可改变行为;
  3. 贴近C语言特性:基于结构体和函数指针实现,无额外依赖,兼容性好。

4.2 局限

  1. 无类型安全:需手动保证“函数指针参数”与“结构体类型”匹配,编译器不报错(如将Counter*传给Comparator*会导致运行时错误);
  2. 语法繁琐:调用时需显式传入结构体指针(如cnt.call(&cnt)),不如C++仿函数简洁;
  3. 泛化能力弱:不支持C++模板的泛型,需为不同类型(如int/float)单独定义仿函数结构体(如IntComparator/FloatComparator)。

5. 常见问题(FAQ)

Q1:为什么调用仿函数时必须传结构体指针(如cnt.call(&cnt))?

A:因为函数指针需要通过指针访问结构体内部的状态变量(如count/is_ascending)。若传值(cnt.call(cnt)),会创建结构体副本,修改的是副本状态,原实例状态不变。

Q2:能否将仿函数结构体定义为指针类型(如Counter*)?

A:可以。若需动态分配内存(如在堆上创建仿函数),可将初始化函数改为返回指针:

// 动态创建计数器仿函数(堆内存)
Counter* Counter_Create_Dynamic(int initial, int step) {
    Counter* cnt = (Counter*)malloc(sizeof(Counter));
    if (cnt != NULL) {
        cnt->count = initial;
        cnt->step = step;
        cnt->call = Counter_Increment;
    }
    return cnt;
}

注意:动态创建后需手动调用free(cnt)释放内存,避免内存泄漏。

Q3:函数指针的语法太复杂,有没有简化方式?

A:可通过typedef为函数指针定义别名,简化代码。如示例2中:

// 简化前:直接在结构体中定义函数指针
typedef struct Comparator {
    int is_ascending;
    int (*compare)(struct Comparator* self, int a, int b);
} Comparator;

// 简化后:先typedef函数指针,再在结构体中使用别名
typedef int (*CompareFunc)(struct Comparator* self, int a, int b);
typedef struct Comparator {
    int is_ascending;
    CompareFunc compare; // 更简洁
} Comparator;

6. 扩展场景

6.1 带多状态的仿函数

示例:实现“带范围限制的计数器”,状态包括count(当前值)、min(最小值)、max(最大值),调用时若超出范围则触发边界处理:

typedef struct RangeCounter {
    int count;
    int min;
    int max;
    int (*call)(struct RangeCounter* self); // 调用时自增,超出范围返回-1
} RangeCounter;

int RangeCounter_Increment(RangeCounter* self) {
    if (self->count >= self->max) {
        printf("已达最大值!\n");
        return -1;
    }
    self->count++;
    return self->count;
}

// 初始化:初始值0,范围[0,5]
RangeCounter RangeCounter_Create(int min, int max) {
    RangeCounter cnt;
    cnt.count = min;
    cnt.min = min;
    cnt.max = max;
    cnt.call = RangeCounter_Increment;
    return cnt;
}

6.2 仿函数数组

可将多个仿函数实例存入数组,实现“批量调用”。如多个不同步长的计数器:

int main() {
    // 创建3个不同步长的计数器
    Counter counters[3] = {
        Counter_Create(0, 1),
        Counter_Create(0, 2),
        Counter_Create(0, 3)
    };
    
    // 批量调用仿函数
    for (int i = 0; i < 3; i++) {
        printf("计数器%d第1次调用:%d\n", i+1, counters[i].call(&counters[i]));
    }
    // 输出:
    // 计数器1第1次调用:1
    // 计数器2第1次调用:2
    // 计数器3第1次调用:3
    return 0;
}

到此这篇关于C语言仿函数(Functor)实现示例的文章就介绍到这了,更多相关C语言仿函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入解析Radix Sort基数排序算法思想及C语言实现示例

    深入解析Radix Sort基数排序算法思想及C语言实现示例

    基数排序和桶排序、计数排序共同是三种最常用的线性排序算法,这里我们就来深入解析Radix Sort基数排序算法思想及C语言实现示例,需要的朋友可以参考下
    2016-07-07
  • C语言深入讲解内存操作问题

    C语言深入讲解内存操作问题

    程序运行的目的是为了得到特定的结果,计算机本质上是用于计算的,既然是用于计算,就需要参与计算的数据,那这些数据就存储在内存中,计算之前参与运算的数据以及运算之后得到的数据,都存储在内存中,所以对内存操作的掌握就尤为重要,下面我们一起来看看
    2022-04-04
  • C语言 简单粗暴的笨方法找水仙花数

    C语言 简单粗暴的笨方法找水仙花数

    这篇文章介绍了C语言找水仙花数最原始的笨方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-02-02
  • C++实现动态数组功能

    C++实现动态数组功能

    这篇文章主要为大家详细介绍了C++实现动态数组功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-11-11
  • OpenGL绘制贝塞尔曲线

    OpenGL绘制贝塞尔曲线

    这篇文章主要为大家详细介绍了OpenGL绘制贝塞尔曲线,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • C/C++ 格式说明符及其用法

    C/C++ 格式说明符及其用法

    在 C/C++ 中,格式说明符(Format Specifiers)主要用于 printf()、scanf() 等输入输出函数中,用于控制数据的格式化输入和输出,下面给大家介绍C/C++ 格式说明符及其用法,感兴趣的朋友一起看看吧
    2025-05-05
  • C语言文字艺术之数据输入输出

    C语言文字艺术之数据输入输出

    这篇文章主要介绍了C语言文字艺术之数据输入输出,C语言的语句用来向计算机系统发出操作指令。一条语句编写完成经过编译后产生若干条机器指
    2022-07-07
  • 使用C++手搓一个TCP连接管理器

    使用C++手搓一个TCP连接管理器

    这篇文章主要为大家详细介绍了如何使用C++手搓一个TCP连接管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-09-09
  • C++中的移动构造函数及move语句示例详解

    C++中的移动构造函数及move语句示例详解

    这篇文章主要给大家介绍了关于C++中移动构造函数及move语句的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例

    Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例

    这篇文章主要介绍了Qt GUI图形图像开发之QT表格控件QTableView详细使用方法与实例,需要的朋友可以参考下
    2020-03-03

最新评论