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

重写printf时使用void *出现类型不兼容警告的解决咨询

解决函数指针数组类型不兼容的编译警告

首先说为啥会出这些警告:C语言里函数指针的签名必须完全匹配,你定义的数组是int (*functions[])(void *),意思是每个元素都是「接收void*参数、返回int」的函数指针,但你放进去的my_putchar是void (*)(char)、my_put_nbr是int (*)(int),这些函数的参数类型、返回值类型和数组要求的不匹配,C语言不允许这种隐式转换,所以就报了不兼容指针的警告。

void*确实可以作为通用指针存各种变量的地址,但它没法直接让不同签名的函数指针互相兼容——函数指针的类型是由参数列表和返回值共同决定的,和普通指针不是一回事。

给你两种适合新手的解决方法:

方法一:给每个函数写适配层(统一函数签名)

给每个输出函数写一个「包装函数」,让它们都符合int (*)(void*)的签名,在包装函数里把void*转换成对应的类型再调用原函数:

// 适配my_putchar
int my_putchar_wrap(void *arg) {
    char c = *(char*)arg;
    my_putchar(c);
    return 1; // 返回输出的字符数,和其他函数统一
}

// 适配my_put_nbr
int my_put_nbr_wrap(void *arg) {
    int num = *(int*)arg;
    return my_put_nbr(num);
}

// 适配my_putstr
int my_putstr_wrap(void *arg) {
    const char *str = (const char*)arg;
    return my_putstr(str);
}

// 适配put_perc(它不需要参数,所以忽略arg)
int put_perc_wrap(void *arg) {
    (void)arg; // 避免未使用参数的警告
    my_putchar('%');
    return 1;
}

// 适配my_put_float
int my_put_float_wrap(void *arg) {
    double f = *(double*)arg;
    return my_put_float(f);
}

然后修改read_print里的函数数组,换成这些包装函数:

int (*functions[])(void *) = {my_putchar_wrap, my_put_nbr_wrap,
    my_put_nbr_wrap, my_putstr_wrap, put_perc_wrap, my_put_float_wrap};

同时注意,用va_arg获取参数时,要先拿到正确类型的值,再把它的地址传给包装函数(因为包装函数接收的是void*):

// 替换原来的functions[k](va_arg(args, void *));
switch(guide[k]) {
    case 'c': {
        char c = va_arg(args, int); // char会被提升为int,所以用int接收
        functions[k](&c);
        break;
    }
    case 'd': case 'i': {
        int num = va_arg(args, int);
        functions[k](&num);
        break;
    }
    case 's': {
        const char *str = va_arg(args, const char*);
        functions[k]((void*)str); // 字符串本身就是指针,直接转void*
        break;
    }
    case '%':
        functions[k](NULL); // 不需要参数,传NULL
        break;
    case 'f': {
        double f = va_arg(args, double);
        functions[k](&f);
        break;
    }
}

方法二:用switch-case替代函数指针数组(更简单直观)

对于初学者来说,直接用switch-case判断格式符,调用对应函数反而更易懂,还能彻底避免函数指针类型问题:

修改read_print函数:

int read_print(const char *s, int i, va_list args)
{
    char fmt = s[i + 1];
    switch(fmt) {
        case 'c': {
            char c = va_arg(args, int); // char在可变参数中会被提升为int
            my_putchar(c);
            return 1;
        }
        case 'd':
        case 'i': {
            int num = va_arg(args, int);
            return my_put_nbr(num);
        }
        case 's': {
            const char *str = va_arg(args, const char*);
            return my_putstr(str);
        }
        case '%':
            my_putchar('%');
            return 1;
        case 'f': {
            double f = va_arg(args, double);
            return my_put_float(f);
        }
        default:
            // 处理未知格式符,比如直接输出%和未知字符
            my_putchar('%');
            my_putchar(fmt);
            return 2;
    }
}

这种方法不需要维护函数指针数组,逻辑更直接,新手调试起来也更方便。

顺便提几个你的代码里的小问题

  1. put_perc函数声明返回int,但没有写return语句,要补上return 1;
  2. read_print函数声明返回int,也需要返回对应的值(比如输出的字符数)
  3. 标准C里main函数应该是int main(void),不是void main(void),最后要加return 0;

内容的提问来源于stack exchange,提问作者The Hollow Owl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 02:37:36