【问题标题】:How does void* work as a universal reference type?void* 如何作为通用引用类型工作?
【发布时间】:2018-02-01 10:07:05
【问题描述】:

来自 Scott 的 Programming Language Pragmatics

用于系统编程,或方便编写 通用容器(集合)对象(列表、堆栈、 队列、集合等)保存对其他对象的引用,有几个 语言提供了一个通用的引用类型。 在 C 和 C++ 中,这 类型称为 void *。在 Clu 中,它被称为 any;在 Modula-2 中, 地址;在 Modula-3 中,reany;在 Java 中,对象;在 C# 中,对象。

在 C 和 C++ 中,void * 如何作为通用引用类型工作?

void * 始终只是指针类型,而通用引用类型包含所有值,包括指针和非指针。所以我看不出void *是一个通用引用类型。

谢谢。

【问题讨论】:

  • “它是如何工作的”具体是什么意思?
  • void * 始终是指针类型,而通用引用类型包含所有值,包括指针和非指针
  • 它可以指向任何东西。但是,您必须先告诉编译器它是什么,然后才能真正了解它所指向的内容。
  • 指针是最接近 C 中类似引用的概念的方法。
  • @Leushenko 书中的引述清楚地表明,这里的“参考”是通用的计算机科学术语,而不是专门针对 C++ 的参考。

标签: c++ c types


【解决方案1】:

void* 指针通常会保存任何不是 C++ 成员指针的指针。这在实践中相当不方便,因为您需要将其转换为另一种指针类型才能使用它。您还需要将其转换为与 转换时相同的指针类型以生成 void*,否则您将面临未定义行为的风险。

一个很好的例子是qsort function。它将void* 指针作为参数,这意味着它可以指向任何内容的数组。传递给qsort 的比较函数必须知道如何将两个void* 指针转换回数组元素的类型,以便比较它们。

【讨论】:

  • "void* 指针通常会保存任何指针..." --> void* 可能不足以“保存”函数指针。我同意Oliver Charlesworth 将函数指针保存在void* 中的尝试是UB。
  • @chux - 我相信这是 UB。
  • 非常非常轻微的挑剔(如果我是对的)“您还需要将其转换为与其转换来源相同的指针类型”,在你使用它之前。我认为您可以同时转换为您喜欢的任何指针类型。不过,我认为这比任何实际操作都更具语言层级。
  • @chux 你能扩展函数指针的限制吗?我记得唯一奇怪的是分段 x86 架构上的长指针。
  • @MarkRansom 您的分段 x86 架构的内存是可能的宽函数指针(例如 32 位)和窄 void*(例如 16 位)的示例。 IAC, C (§6.3 ...) 使用void* 指定到/从object 指针的转换,并且通过省略,到/从函数指针的转换是UB。一种类型的函数指针与另一种类型的函数指针相互转换。所以union { void *v; int (*f)(); } 接近于一个通用的“ref”类型。
【解决方案2】:

您困惑的关键在于,void * 的实例和 Modula-3 的 refany 的实例,以及任何其他语言的“可以引用任何东西”类型的实例,包含 它所指的对象。 void * 类型的变量始终是指针,refany 类型的变量始终是引用。但是他们引用的对象可以是任何类型。

编程语言理论的纯粹主义者会告诉您,C 根本没有引用,因为指针不是引用。它有一个几乎通用的 pointer 类型,void *,它可以指向任何类型(包括整数、聚合和其他指针)的 object。作为一个常见但不普遍的扩展,它也可以指向任何函数(函数不是对象)。

纯粹主义者还会告诉你,C++ 确实没有具有(几乎)通用指针类型,因为它的类型系统更严格,并且也没有通用引用类型。 p>

他们还会说您正在阅读的这本书的术语很草率,他们会警告您不要将任何这样的书当作关于术语问题或任何其他问题的福音真理。相反,您应该广泛阅读书籍、CS 期刊和会议论文集(统称为“文献”),直到您“听到”普遍认可的术语、特定于子学科或社区的术语练习等等。

最后他们会提醒你,C 和 C++ 是两种不同的语言,任何同时谈到它们的人要么掩盖了它们的区别(这可能与上下文相关,也可能不相关),已经过时了几十年,或两者兼而有之。

【讨论】:

    【解决方案3】:

    可能的原因是您可以获取任何类型的任何变量的地址并将其转换为void*

    【讨论】:

      【解决方案4】:

      你知道对象的实际类型是通过一个无声的契约来实现的。

      因此您可以在容器中存储不同类型的元素,但您需要以某种方式知道在取回元素时是什么,以便正确解释它们。

      void* 提供的唯一便利在于它是惯用的,即很明显,取消引用指针没有意义,void* 可以隐式转换为任何指针类型。那是为了c/

      在 c++ 中,这被称为首选类型擦除技术。或特殊类型,如any(也有一个增强版本。)

      【讨论】:

      • 不在 C++ 中。如果这个问题被标记为 C 和 C++,这个问题就没有意义了。
      • @NeilButterworth 我在补充/澄清中进行了编辑。
      【解决方案5】:

      void* 不再是只是一个指针。因此,它持有一个对象的地址(或一个数组和类似的东西)

      当你的程序运行时,每个变量都应该在内存中拥有自己的地址,对吧?而指针就是指向那个地址的东西。

      通常情况下,每种类型的指针都应该是相同类型的对象int b = 5; int* p = &b;。但是这样的话你就知道是什么类型了,就是specific type

      但有时,您只想知道它在内存中的某个位置存储了一些东西,并且您知道该地址的“类型”,您可以轻松地进行转换。例如,在我正在学习的 OpenCV 库中,用户可以将参数传递给很多函数,而不是声明全局变量,并且大多数函数在 callback 函数中使用,如下所示: void onChange(int v, void *ptr) 在这里,库并不关心ptr指向什么,它只知道当你调用函数时,如果你传递一个地址来喜欢这个onChange(5,&b),那么你必须在处理之前将ptr转换为相同的类型它int b = static_cast<int*>(ptr);

      【讨论】:

        【解决方案6】:

        Understanding pointers from Richard Reese 的解释可能会有所帮助

        指向 void 的指针是一个通用指针,用于保存对任何数据类型的引用。

        它有两个有趣的特性:

        • 指向 void 的指针与指向 char 的指针具有相同的表示和内存对齐方式

        • 指向 void 的指针永远不会等于另一个指针。但是,分配了 NULL 值的两个 void 指针将相等。

        任何指针都可以分配给指向 void 的指针。然后可以将其转换回其原始指针类型。发生这种情况时,该值将等于原始指针值。

        这在以下序列中进行了说明,其中指向 int 被分配给指向 void 的指针,然后返回指向 int 的指针

        #include<stdio.h>
        
        void main()
        {
            int num = 100;
            int *pi = &num;
        
            printf("value of pi is %p\n", pi);
        
            void* pv = pi;
            pi = (int*)pv;
        
            printf("value of pi is %p\n", pi);
        }
        

        指向 void 的指针用于数据指针,而不是函数指针

        【讨论】:

        • “指向 void 的指针永远不会等于另一个指针” - 这是真的吗?
        • "%p" 匹配 void *,而不是 int *,如本示例代码所示。 printf("value of pi is %p\n", pi); 是 UB,尽管它通常“有效”。
        猜你喜欢
        • 2023-01-12
        • 1970-01-01
        • 1970-01-01
        • 2015-06-29
        • 1970-01-01
        • 2017-06-09
        • 1970-01-01
        • 2020-04-18
        • 2018-12-22
        相关资源
        最近更新 更多