gcc对void型指针的类型检查有问题?

本文探讨了C语言中void**与void*类型的隐性不匹配问题,以及为何GCC不会默认警告。通过实例展示如何避免编译错误,并提倡正确处理库接口设计,强调在C语言中避免void**的使用和C++编译器的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

前一段时间遇到一个问题,我调用了一个隔壁部门的库,这个库在调用前要创建context,程序退出前要销毁context,问题是销毁的时候,出现段错误了。
经过一番定位,发现是库的销毁接口跟库的常规处理接口稍有差别

typedef void *xxx_handle;
//常规接口
int xxx_proc(xxx_handle h, int arg);
//创建接口
xxx_handle xxx_create();
//销毁接口
int xxx_destroy(xxx_handle *ph);

销毁接口的第一个入参类型不是void*,而是void **
等等,为什么编译没有告警指针类型不匹配?

gcc对void *的检查

下面这行代码不会有任何告警,但指针类型其实是不匹配的:

#include<stdio.h>

typedef void *handle;

void f(handle *ph) {
}

int main() {
    handle h = NULL;
    f(h);
    return 0;
}

这说明,gcc不会告警一级void指针和二级void指针的类型不匹配!

尝试用g++编译问题代码,直接报error:

test_void_ptr.c: In function ‘int main()’:
test_void_ptr.c:10:8: error: invalid conversion from ‘handle {aka void*}’ to ‘void**’ [-fpermissive]
     f(h);
        ^
test_void_ptr.c:5:6: note:   initializing argument 1 of ‘void f(void**)’
 void f(handle *ph) {
      ^

看样子是C语言编译器特有的现象,C还是太permissive了。

尝试规避

但是你如果把void关键字替换成int,其他不做修改,则gcc立马报incompatible pointer type告警:

test_void_ptr.c: In function ‘main’:
test_void_ptr.c:10:7: warning: passing argument 1 of ‘f’ from incompatible pointer type [-Wincompatible-pointer-types]
     f(h);
       ^
test_void_ptr.c:5:6: note: expected ‘int **’ but argument is of type ‘handle {aka int *}’
 void f(handle *ph) {
      ^

那能否统一用int*做handle来规避上述问题呢?恐怕不太行,因为函数接口将形参定义成void *就是为了方便调用者传参时不用进行强制类型转换,改成int*后,就必须强转了。

怎么解决?

我试了以下方法:

  1. voidtypedef成VOID以创造新的基类型,不好使
  2. handle *typedef成phandle以显式区别2种void指针类型,也不行

看样子只能程序员自己注意了?好悲催啊!
等等,有没有可能,编译器就是不想区分void*void **呢?毕竟指针本身就是可以值传递的,为什么还要脱裤子放屁地搞个二级指针呢?

更改编程习惯

回头看我们的库接口设计,是有问题的,create返回context的地址给调用者,那么调用者在destroy执行完毕后当然有义务将记录的context置空,但是库作者为了“易用性”,选择在destroy里自己置空,而这势必要获取调用者记录context地址的指针的地址,这就“越界”了!
正确的做法是destroy只销毁context,至于调用者可能忘记置空context指针导致的内存访问问题,由调用者自己操心

总结

  1. C语言中尽量不要使用void **参数
  2. 用户态逻辑尽量用C++来写,不能改的话,至少用C++编译器来编译吧。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值