间接调用 static 函数的方法

目录

1. 步骤

1.1 定义 static 函数

1.2 定义一个全局(非 static )的函数指针或提供一个返回该函数指针的接口

1.3 定义普通(非 static )接口函数

1.4 在其他文件中通过接口或回调调用

1.4.1 采用全局函数指针方案

1.4.2 使用 getter 函数

1.4.3 采用接口封装(wrapper)方案

2. 示例

2.1 最小示例

2.2 完整示例


背景:在复刻一个关于锂电池(5-16串)BMS电池管理系统的项目时,我想要利用XShell(通过串口通信)实时打印电池各项参数,但对应的 InfoPrint() 函数是 static,但我又不想破坏其静态属性,故选择间接调用static函数的方法。具体技巧如下:

在 C 语言中,被 static 修饰的函数具有内部链接,仅在定义它的源文件中可见。也就是说,不能在其他文件中通过函数名直接调用它。为实现间接调用,可以在定义 static 函数的文件中暴露一个函数指针或封装接口,并在需要调用的文件中通过该接口/指针进行调用。

1. 步骤

1.1 定义 static 函数

static void hidden_func(void) { /* ... 函数实现 ... */ }

1.2 定义一个全局(非 static )的函数指针或提供一个返回该函数指针的接口

可以在此文件中声明一个函数指针并在初始化函数中赋值:

void (*hidden_ptr)(void) = NULL;
void init_hidden(void) {
    hidden_ptr = hidden_func;  // 指针指向静态函数
}

或者提供一个 getter 函数返回指向 static 函数的指针:

typedef void (*FuncPtr)(void);
FuncPtr get_hidden_func(void) {
    return hidden_func;
}

这样做的原理是:虽然 hidden_func本身不能被链接器导出,但在同一文件内可以获取它的地址并赋值给全局指针或返回出去。这类似于注册回调的做法:在静态函数所在模块中,把静态函数的指针登记到一个可访问的位置。

1.3 定义普通(非 static )接口函数

该函数在内部调用静态函数,使其可被外部调用。例如:

static void hidden_func(void) { /* ... */ }
void call_hidden(void) {
    hidden_func();  // 在接口函数中调用静态函数
}

这样外部文件只需调用 call_hidden() 即可间接执行 hidden_func()

1.4 在其他文件中通过接口或回调调用

在需要调用 static 函数的文件中,通过 extern 引用前面定义的指针或接口,然后调用:

1.4.1 采用全局函数指针方案

在其他文件中声明并初始化:

extern void (*hidden_ptr)(void);
extern void init_hidden(void);
int main(void) {
    init_hidden();      // 初始化,使指针指向 static 函数
    hidden_ptr();       // 通过指针调用
    return 0;
}

1.4.2 使用 getter 函数

只需调用该函数获得指针:

extern FuncPtr get_hidden_func(void);
int main(void) {
    FuncPtr f = get_hidden_func();
    f();               // 间接调用
    return 0;
}

1.4.3 采用接口封装(wrapper)方案

直接调用在静态文件中定义的普通接口函数:

extern void call_hidden(void);
int main(void) {
    call_hidden();     // 调用接口函数,在内部执行 static 函数
    return 0;
}

无论哪种方式,关键都是通过指针转发或调用中间接口实现:先在静态函数所在文件把函数地址保存到可导出的指针或容器里,再由其他文件调用该指针/接口。

2. 示例

2.1 最小示例

演示 static 函数通过函数指针被间接调用:

/* module.c:定义静态函数并暴露接口 */
#include <stdio.h>

/* 内部静态函数,文件内可见 */
static void hidden(void) {
    printf("Hidden static function\n");
}

/* 全局函数指针和初始化函数 */
void (*hidden_ptr)(void) = NULL;  // 声明指针
void init_hidden(void) {
    hidden_ptr = hidden;  // 指针指向静态函数
}
/* module.h */
extern void (*hidden_ptr)(void);
void init_hidden(void);
/* main.c:通过接口调用隐藏的 static 函数 */
#include "module.h"

int main(void) {
    init_hidden();   // 将指针赋值为静态函数的地址
    hidden_ptr();    // 通过指针调用静态函数
    return 0;
}

以上代码中,hidden() 被声明为 static,只能在 module.c 中被直接访问。但我们在同一文件内将其地址赋给了全局指针 hidden_ptr。在 main.c 中先调用 init_hidden() 初始化指针,然后通过 hidden_ptr() 间接调用该静态函数。

2.2 完整示例

module.c 中定义多个 static 计算函数(带两个参数),并通过一个结构体数组来注册它们。外部模块只拿到结构体中的函数指针和名称,就能调用这些本该“私有”的 static 函数。

/* module.h */
#ifndef MODULE_H
#define MODULE_H

#include <stddef.h>

/* 函数签名:两个 int 参数,返回 int */
typedef int (*ComputeFunc)(int a, int b);

/* 注册表项:名称 + 函数指针 */
typedef struct {
    const char    *name;
    ComputeFunc    func;
} ComputeEntry;

/* 外部可见的注册表和大小 */
extern ComputeEntry compute_table[];
extern size_t       compute_table_size;

/* 初始化函数:在程序启动时调用,将 static 函数地址填入表中 */
void init_compute_table(void);

#endif /* MODULE_H */
/* module.c */
#include "module.h"

/* —— 私有静态函数 —— */
static int add(int a, int b) {
    return a + b;
}

static int multiply(int a, int b) {
    return a * b;
}

static int subtract(int a, int b) {
    return a - b;
}

/* —— 定义一个不带函数指针的“空壳”表 —— */
ComputeEntry compute_table[] = {
    { "add",      NULL },
    { "multiply", NULL },
    { "subtract", NULL }
};

/* 表的大小 */
size_t compute_table_size = sizeof(compute_table) / sizeof(compute_table[0]);

/* 初始化:把静态函数的地址写进去 */
void init_compute_table(void) {
    compute_table[0].func = add;
    compute_table[1].func = multiply;
    compute_table[2].func = subtract;
}
/* main.c */
#include <stdio.h>
#include "module.h"

int main(void) {
    /* 第一步:初始化,让指针指向 static 函数 */
    init_compute_table();

    /* 第二步:遍历表,调用各个函数 */
    for (size_t i = 0; i < compute_table_size; ++i) {
        ComputeEntry *e = &compute_table[i];
        if (e->func) {
            int x = 10, y = 3;
            int result = e->func(x, y);
            printf("%s(%d, %d) = %d\n", e->name, x, y, result);
        }
    }

    return 0;
}

输出如下:

add(10, 3) = 13
multiply(10, 3) = 30
subtract(10, 3) = 7

注:

  1. 封装性addmultiplysubtract 都是 static,只能在 module.c 内部访问。

  2. 注册表compute_table 数组对外可见,但最初其 func 指针全为 NULL,调用者没法直接拿到静态函数地址。

  3. 初始化init_compute_table() 在文件内部把每个 hidden 函数地址写入表里,之后外部模块就能通过结构体里的指针来间接调用它们。

  4. 多参数与结构体:示例中函数都带了两个参数,还配合结构体,把名称和指针捆绑,易于扩展(比如动态插件、命令解析、脚本绑定等场景)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值