重写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; } }
这种方法不需要维护函数指针数组,逻辑更直接,新手调试起来也更方便。
顺便提几个你的代码里的小问题
put_perc函数声明返回int,但没有写return语句,要补上return 1;read_print函数声明返回int,也需要返回对应的值(比如输出的字符数)- 标准C里
main函数应该是int main(void),不是void main(void),最后要加return 0;
内容的提问来源于stack exchange,提问作者The Hollow Owl
相关产品推荐
相关产品推荐

