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

Way to supress warning for implicit typecasting from void ** to another **?

我有一个小项目,其中包括处理指针数组,为了方便我制作了一个 .h 文件来处理它,方法是使用 void 指针进行操作,然后 returns 最终产品,即函数原型会类似于 void **ptr_array_add(void **ptr_array, void *new_ptr);。但是,每当我使用此函数而没有明确地将我的指针类型转换为 void ** 并返回到它们的原始类型时,gcc 会在转换时抛出警告。

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

谢谢

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

Is what I'm doing unsafe?

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

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

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

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

所有强制转换现在都在函数内部。 您的用户代码必须将数组保持为 void*.

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

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

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

您也可以(在 C 中)为 return 值使用输出参数,如果您可以完全更改函数签名:

// 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)
}

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