抑制从 void ** 到另一个 ** 的隐式类型转换的警告的方法?

Posted

技术标签:

【中文标题】抑制从 void ** 到另一个 ** 的隐式类型转换的警告的方法?【英文标题】:Way to supress warning for implicit typecasting from void ** to another **? 【发布时间】:2022-01-11 08:39:57 【问题描述】:

我有一个小项目,其中包括处理指针数组,为了方便我制作了一个 .h 文件,该文件通过使用 void 指针执行操作然后返回最终产品来处理它,即函数原型类似于@987654323 @。然而,每当我使用这个函数而不明确地将我的指针类型转换为void ** 并返回到它们的原始类型时,gcc 就会在转换时抛出警告。

我正在做的事情不安全吗?我假设 gcc 给我警告一定是有原因的,但据我所知,void ** 与任何其他类型的指针数组相同,因为所有指针都是 8 个字节长(或者不管它们有多长,关键是它们的长度都是一样的),如果我所做的没有问题,有没有办法在每次调用这些函数时无需添加 20 个类型转换字符来抑制警告?

谢谢

【问题讨论】:

如果将指针别名为其他指针类型,则行为未定义,即使大小相同。演员表只是使警告静音,它不会使代码安全。 void * 之间进行投射是安全的。这种安全性不会扩展到void ** 询问如何抑制警告通常是不好的,可能是危险的。您应该问的问题是如何编写正确的、受支持的代码。为此,您必须为您正在做的事情提供更多背景信息,最好是minimal reproducible example。 当你edit你的问题对cmets做出反应时,也请告诉我们你需要多少不同的指针类型。你考虑过使用联合吗? C 允许对象指针隐式转换为类型void *。涉及指针的所有其他转换都需要强制转换。 GCC 将隐式执行额外的转换,但它会警告它们(至少在某些情况下)。这基本上是一个可移植性警告,诊断将指定控制该特定警告的编译器选项。您所做的绝对是不合格和可疑的,但如果没有更多信息,我们无法判断除可移植性之外的具体风险。 【参考方案1】:

C 类型系统只有一种特殊的泛型指针,那就是void*,只有那个。但是,这不适用于“递归”; void** 没有什么特别之处。因此,您可以分配给void** 的唯一类型是另一个void**void* 的地址。

因此,为了使用您发布的函数,您需要在调用方声明一个void*,将其传递给函数,然后将void* 转换为相关的对象指针:

void* tmp;
... ptr_array_add(&tmp, ...);
int* some_other_pointer = tmp; // assuming that the actual data type is indeed int

我正在做的事情不安全吗?

是的,如果指针被取消引用为其他类型而不是它实际指向的类型,则通过强制转换为“关闭警告”来强制执行无效指针转换将导致未定义的行为。具体问题是严格的指针别名、指针大小和指针的陷阱表示,所有这些在理论上都会造成细微的错误和崩溃。

但实际上,非外来系统上的大多数指针很可能在内部具有相同的大小和表示形式。像 CUDA 这样的一些库甚至使用 void** 和其他指针对指针之间的脏转换作为 API 的一部分。但是,当您这样做时,C 标准不支持保证任何确定性行为。

【讨论】:

【参考方案2】:

如果这是一个指针一个接一个的数组,您可以更改签名以使用 void*。我做了一个简单的例子,将新指针写入数组的第二个条目并返回数组的旧开始。

void* ptr_array_add(void* ptr_array, void* new_ptr) 
    void** entries = (void**)ptr_array;
    entries[1] = new_ptr;
    return (void*)entries;

现在所有的演员表都在函数内部。 您的用户代码必须将数组保持为 void*。

如果您还想将返回值转换为特定类型,例如int**,不能自动完成。

您必须为您使用的每种类型创建特定的包装器。 有了 C++ 编译器,就会有模板。

仅对于 C,您可以使用预处理器宏来自动创建包装函数。

如果您可以完全更改函数签名,您也可以(在 C 中)使用 out 参数作为返回值:

// out_ptr_array points to a location, where the result is stored
// in your terminology it would be a void***, but we actually use void*
// in_ptr_array is void**, but we actually use void*
// new_ptr is void*

void ptr_array_add(void* out_ptr_array, void* in_ptr_array, void* new_ptr) 
    void** entries = (void**)in_ptr_array;
    entries[1] = new_ptr;
    void** returnarray = (void**)out_ptr_array;
    *returnarray = entries; // of course we can also return a new/another address to an array



// no warnings!
#include <stdio.h>
int main() 
    int* array[3]; // array of pointers to int
    int** arrayptrin; // location of array
    arrayptrin = &array[0];

    int** arrayptrout; // return value of function
    int a = 4;         // value
    int* pa = &a;      // pointer to value for adding

    // call function with parameters
    // where to store result (new array location?), array location, new pointer
    ptr_array_add(&arrayptrout, arrayptrin, pa);

    int* pb = arrayptrout[1]; // read one pointer
    printf("Result: %d\n", *pb); // print, value on pointer location (prints: 4)

在这个例子中,我们不需要任何头函数之外的强制转换。根本没有发出警告。

【讨论】:

以上是关于抑制从 void ** 到另一个 ** 的隐式类型转换的警告的方法?的主要内容,如果未能解决你的问题,请参考以下文章

C++,bool 转换是不是总是退回到 void* 的隐式转换?

从枚举类型 'enum UIViewAnimationOption 到不同枚举类型 uiviewanimation 的隐式转换

25.scala的隐式转换

警告:从枚举类型“UIInterfaceOrientation”到不同枚举类型“UIDeviceOrientation”的隐式转换?

从数据类型varbinary到日期SQL服务器的隐式转换

不允许从数据类型 nvarchar 到 varbinary(max) 的隐式转换