【问题标题】:Casting a void pointer to an arbitrary type pointer将 void 指针转换为任意类型指针
【发布时间】:2012-01-30 14:53:33
【问题描述】:

为了我自己的学习利益,我正在尝试用 C 语言自己实现常见的数据结构。我目前的工作是一个向量,我希望它能够保存一个任意类型(或者至少是类型大小,但这不是 C 语言中真正重要的吗?)。我的结构如下:

struct vector
{
    void *item;
    size_t element_size;
    size_t num_elements;
}

但是,我不明白的是,如果类型应该是任意的,我如何引用 *item 数组中的特定元素。我知道 element_size,但这对索引引用没有帮助(例如 item[5]),因为 void 不是一种类型。我认为将元素称为字节偏移量是最简单的。因此,如果我持有一个大小为 12 的结构向量,item[5] 将在 item* 的 12*5=60 字节处。但是,我不明白如何检索该数据。我知道我想要 item+60 中的 12 个字节,但是如何让编译器理解这一点?我进入预处理器领域了吗?

【问题讨论】:

    标签: c void-pointers


    【解决方案1】:

    sizeof 以字符为单位,因此您可以这样做:

    void *start_of_sixth_element = ((char*)item) + (5 * element_size);
    

    知道每个元素的类型的人可以将start_of_sixth_element 转换为正确的类型以使用它。

    void* 在某种程度上是一个糟糕的选择,因为您不能在标准 C 中使用 void* 指针进行指针运算(有一个 GNU 扩展允许它,但对于可移植代码,您使用 char* 或 @ 987654327@ 至少算术)。

    在应用偏移量 5 之前知道正确类型的代码可以这样做:

    correct_type *sixth_element = ((correct_type *)item) + 5;
    

    void 不是类型

    它是一种类型,只是不是“完整类型”。 “不完整类型”几乎意味着编译器不知道 void* 真正指向的内容。

    【讨论】:

    • 给定一个类型,有没有一种简单的方法可以找出element_size 的正确值? (我正在考虑对齐要求。)
    • @aix:给定类型,element_sizesizeof(type):对象的大小已经包含对齐所需的任何填充。对齐方式取决于item 指向的内存是如何分配的。只要它是用malloc(element_size*num_elements) 分配的(并且乘法不会溢出!)那么就不存在对齐问题,因为malloc 被“保证”返回与任何对象对齐的内存。我使用窃笑引号是因为 SIMD 指令的超大类型通常被实现排除在外。
    • @aix,sizeof 运算符将对齐考虑在内。例如,如果 long 具有 4 字节对齐,则 sizeof 应用于包含 longcharstruct 为 8。
    • 我被建议改用 unsigned int 作为我的内部 item[] 类型,它将保存容器存储的值的地址。你会推荐它而不是使用 void* 吗?
    • @doubleshot:不,不可能将指针值存储在unsigned int 中。在 64 位 Windows 或 Linux 上,地址是 64 位,但 unsigned int 只有 32 位,所以它完全不是入门的。 void*unsigned char* 都可以合理地用于存储任何对象的地址。
    【解决方案2】:

    void * 是任何对象指针类型的通用指针类型。您必须知道它指向什么类型才能正确转换 void * 并取消引用指针。

    这是一个使用不同指针类型的 void * 对象的示例。

    void *p;
    int a = 42, b;
    double f = 3.14159, g;
    
    p = &a;
    b = *(int *) a;
    
    p = &f;
    g = *(double *) f; 
    

    【讨论】:

      【解决方案3】:

      因此,如果我持有一个大小为 12 的结构向量,则 item[5] 将位于 item* 的 12*5=60 字节处。但是,我不明白如何检索该数据。我知道我想要 item+60 中的 12 个字节,但是如何让编译器理解呢?

      假设您的给定类型是foo。正如您已经理解的那样,您需要到达item + (5 * 12) 的地址,然后在将void 指针转换为foo* 后取消引用。

      foo my_stuff = *(foo *)(item + 5 * 12);
      

      如果您可以在编译时确定数据的类型,您也可以使用sizeof()

      foo my_stuff = *(foo *)(item + 5 * sizeof(foo));
      

      【讨论】:

        猜你喜欢
        • 2021-09-20
        • 2017-10-07
        • 2015-02-14
        • 2021-07-22
        • 1970-01-01
        • 1970-01-01
        • 2016-01-12
        • 2012-11-21
        相关资源
        最近更新 更多