You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C语言的TAILQ中添加函数指针

问题分析与解决

你的代码核心问题出在函数指针的处理上,完全没必要用calloc分配内存再memcpy——函数指针本身就是一个存储函数地址的变量,直接赋值就行。另外代码里还有两个低级错误,一起给你修正:

错误点拆解

  1. 函数指针的错误操作:你给entry->callback分配堆内存,再把函数指针的值拷贝进去,这会让entry->callback指向一块堆内存,而不是目标函数的地址,触发回调时必然出错。
  2. 结构体成员访问错误:main里的TAILQ_INIT(&identifier->callback_list)是错的,identifier是全局结构体变量,不是指针,应该用.访问成员,改成TAILQ_INIT(&identifier.callback_list)。
  3. 关键字冲突:register是C语言的关键字,不能用作函数名,得改成register_callback之类的合法名称。

修正后的完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>  // 确保包含TAILQ的头文件

#define OK 0
#define NOK -1
#define errorf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
#define debugf(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__)
#define FREE(ptr) do { if (ptr) free(ptr); ptr = NULL; } while(0)

struct callback_list_entry {
    int (*callback)(void *, unsigned int);
    char *title;
    TAILQ_ENTRY(callback_list_entry) entries;
};
TAILQ_HEAD(callback_list, callback_list_entry);

struct sipc_identifier
{
    unsigned int port;
    int (*broadcast_callback)(void *, unsigned int);
    struct callback_list callback_list;
};

typedef struct sipc_identifier _sipc_identifier;

static _sipc_identifier identifier;

int my_callback(void *prm, unsigned int len)
{
    printf("here is %s: %s len: %d\n", __func__, (char *)prm, len);
    return OK;
}

static int add_callback_to_callback_list(int (*callback)(void *, unsigned int), char *title, _sipc_identifier *identifier)
{
    struct callback_list_entry *entry = NULL;

    if (!callback || !identifier || !title) {  // 补充title的空指针检查
        errorf("args cannot be NULL\n");
        return NOK;
    }

    entry = calloc(1, sizeof(struct callback_list_entry));
    if (!entry) {
        errorf("calloc failed\n");
        return NOK;
    }

    // 直接赋值函数指针,无需分配内存和memcpy
    entry->callback = callback;
    debugf("callback address: %p, entry->callback: %p\n", callback, entry->callback);

    // title的处理是对的,继续保留
    entry->title = calloc(1, strlen(title) + 1);
    if (!entry->title) {
        errorf("calloc failed\n");
        FREE(entry);
        return NOK;
    }
    strcpy(entry->title, title);

    TAILQ_INSERT_HEAD(&identifier->callback_list, entry, entries);
    return OK;
}

// 把register改成register_callback,避免关键字冲突
static int register_callback(char *title, int (*callback)(void *, unsigned int))
{
    if (!title || !callback) {
        errorf("args cannot be NULL\n");
        return NOK;
    }
    return add_callback_to_callback_list(callback, title, &identifier);
}

int main(int argc, char **argv)
{
    // 修正结构体成员访问方式,用.而不是->
    TAILQ_INIT(&identifier.callback_list);
    register_callback("App_A_Registered_title", my_callback);  // 函数名本身就是指针,无需&

    // 可以加个测试触发回调的代码验证
    struct callback_list_entry *entry = TAILQ_FIRST(&identifier.callback_list);
    if (entry) {
        char test_data[] = "test callback";
        entry->callback(test_data, strlen(test_data));
    }

    return OK;
}

关键修改说明

  • 函数指针赋值:直接entry->callback = callback;即可,函数指针和普通指针一样,赋值就能传递地址,不需要额外内存分配。
  • 修正结构体访问:全局变量identifier是实体,用.访问成员,指针才用->。
  • 关键字重命名:把register函数改成register_callback,规避C语言关键字冲突。
  • 补充空指针检查:在add_callback_to_callback_list里加了title的空指针判断,更严谨。

测试这段代码,回调函数能正常触发输出,TAILQ的存储和访问也没问题。

内容的提问来源于stack exchange,提问作者dogus yuksel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:05:40