【问题标题】:Are void * pointers meant for generic typing in C?void * 指针是否用于 C 中的泛型类型?
【发布时间】:2019-08-05 15:33:32
【问题描述】:

我经常看到 void 指针与其他类型的指针来回转换,我想知道为什么,我看到 malloc() 返回的 void 指针也需要在使用之前进行转换。

我对 C 语言非常陌生,并且来自 Python,所以指针是一个非常新的概念。

void * 指针的目的是提供一些“动态”类型吗?

以这段代码为例,它是有效的代码吗?

struct GenericStruct{
    void *ptr;
};

// use void pointer to store an arbitrary type?
void store(struct GenericStruct *strct, int *myarr){
    strct->ptr = (void *) myarr;
}

// use void pointer to load an arbitrary type as int*?
int *load(struct GenericStruct *strct){
    return (int *) strct->ptr;
}

【问题讨论】:

  • C 中没有动态类型。类型描述了所需内存的大小和一些内置函数(如 +/- 等)的行为。指针的大小不取决于类型它指向虽然,但在你的操作系统上。接下来尝试理解指针算术,也许这可以帮助您更好地理解指针。除了来自莫斯科的@Vlad 回答之外,还非常简洁地回答了您的问题。
  • 不需要转换malloc返回的指针。而且你也不应该这样做。

标签: c pointers implicit-conversion void


【解决方案1】:

void * 的目的是为 C 的某些打字规则提供一个受欢迎的例外。除了void *,你不能在没有强制转换的情况下将一种类型的指针值分配给不同指针类型的对象——例如,你不能写

int p = 10;
double *q = &p; // BZZT - cannot assign an int * value to a double *

当分配给不同类型的指针时,你必须显式地转换为目标类型:

int p = 10;
double *q = (double *) &p; // convert the pointer to p to the right type before assigning to q

除了void *

int p = 10;
void *q = &p; // no cast required here.

在 K&R C 的旧时代,char * 被用作“通用”指针类型1 - 内存分配函数 malloc/calloc/realloc 都返回 char *,@ 的回调函数987654330@ 和 bsearch 采用了 char * 参数等,但是因为您不能直接分配不同的指针类型,所以您必须添加显式转换(如果目标不是 char *,无论如何):

int *mem = (int *) malloc( N * sizeof *mem );

到处使用显式强制转换有点痛苦。

1989/1990 标准 (C89/C90) 引入了 void 数据类型 - 这是一种不能存储任何值的数据类型。 void 类型的表达式仅针对其副作用(如果有)2 进行评估。为void * 类型创建了一个特殊规则,以便可以将这种类型的值分配给/从任何其他指针类型而不需要显式强制转换,这使其成为新的“通用”指针类型。 malloc/calloc/realloc 全部更改为返回 void *qsortbsearch 回调现在采用 void * 参数而不是 char *,现在事情变得更清晰了:

int *mem = malloc( sizeof *mem * N );

您不能取消引用 void * - 在上面的示例中,q 的类型为 void *,如果没有强制转换,我们无法获得 p 的值:

printf( "p = %d\n", *(int *)q );

请注意,C++ 在这方面是不同的 - C++ 不会特别对待 void *,并且需要显式强制转换以分配给不同的指针类型。这是因为 C++ 提供了 C 没有的重载机制。


  1. 每个对象类型应该可以映射到char 的数组。
  2. 在 K&R C 中,所有函数都必须返回一个值 - 如果您没有显式键入函数,编译器会假定它返回 int。这使得很难确定哪些函数实际上意味着返回一个值,而哪些函数只有副作用。 void 类型对于键入不打算返回值的函数很方便。

【讨论】:

    【解决方案2】:

    C 因缺少函数重载而受苦。因此,大多数 C“通用”函数,例如 qsortbsearch 使用指向 void * 的指针,以便能够处理不同类型的对象。

    在 C 中,您不需要将任何类型的指针强制转换为 void * 类型的指针。并且任何类型的指针都可以用 void * 类型的指针分配,而无需强制转换。

    所以在 C 中,您的代码 sn-p 中的函数可以像这样重写

    void store(struct GenericStruct *strct, int *myarr){
        strct->ptr = myarr;
    }
    
    int *load(struct GenericStruct *strct){
        return strct->ptr;
    }
    

    【讨论】:

    • 哦,对了,我认为转换是必要的。
    • @Jeacom 在 C++ 中需要从 void * 类型的指针转​​换为任何其他类型的指针。
    • @Jeacom 指针 转换,但如答案中所述,不需要强制转换 - 指针会自动转换。 (@Vlad 提到的 C++ 规则不同。)
    【解决方案3】:

    void * 指针的目的是提供一些“动态”类型吗?

    不,因为 C 没有动态类型。但另一方面,您的标题是正确的——void * 大致用作 generic 指针类型,因为该类型的指针可以指向任何类型的对象。但是没有动态性,因为该术语通常应用于此类上下文,因为 void * 类型的值不包含有关它们指向的对象类型的任何信息。

    因此,使用的是指针,指向的对象的类型无关紧要(出于给定目的)。然而,在某些情况下,被指向对象的 size 确实很重要,因此有时会与此类指针一起提供。标准的qsort() 函数是一个典型的例子。在其他情况下,例如通用链表,用户可以通过某种方式知道所指向的对象类型。

    然而,正如@Vlad 已经观察到的那样,在 C 中不需要在 void * 和其他对象指针类型之间进行转换。这种转换是在赋值时自动执行的,在其他使用与赋值相同的规则的上下文中。

    【讨论】:

      猜你喜欢
      • 2016-03-11
      • 1970-01-01
      • 2013-06-13
      • 2011-01-28
      • 2020-11-19
      • 2010-10-22
      • 2011-02-05
      • 1970-01-01
      • 2023-03-16
      相关资源
      最近更新 更多