【问题标题】:Void pointers to struct pointers in CC中指向结构指针的无效指针
【发布时间】:2012-05-03 17:54:09
【问题描述】:

我正在学习 C,主要是通过 K&R,但现在我找到了面向对象的 C pdf 教程并且很着迷。我正在经历它,但我的 C 技能/知识可能无法胜任这项任务。 这是教程:http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

我的问题来自于查看 pdf 前几章中的许多不同功能。下面是其中之一。 (pdf第14页)

void delete(void * self){
     const struct Class ** cp = self;

     if (self&&*cp&&(*cp)->dtor)
                self = (*cp)->dtor(self);
     free(self);

 }

dtor 是一个析构函数指针。但是对于我的问题来说,这方面的知识并不是真正必要的。

  • 我的第一个问题是,为什么 **cp 是常数?是否有必要或只是为了让代码编写者不会意外做出任何破坏性的事情?
  • 其次,为什么 cp 是指向指针的指针(双星号?)。 struct 类在 pdf 的第 12 页上定义。我不明白为什么它不能是单个指针,因为我们似乎将 self 指针转换为 Class 指针。
  • 第三,如何将 void 指针更改为类指针(或指向类指针的指针)?我觉得这个问题最能说明我对C的理解不足。我脑海中想象的是一个void指针占用了一定的内存,但它必须小于Class指针,因为一个Class有很多“东西”在里面。我知道 void 指针可以“强制转换”为另一种类型的指针,但我不明白如何,因为可能没有足够的内存来执行此操作。

提前致谢

【问题讨论】:

  • @Joe 绝对没有错。良好的 C 编程技能非常宝贵,在快速编写脚本的今天很难找到。
  • @Joe - 我认为这意味着 ANSI C89,较新的 K&R 书中讨论了它。示例中的代码不是 K&R C IIRC。
  • 我正在阅读 K&R ANSI(我认为从 1989 年开始,如您所说。)代码示例来自 OOC pdf

标签: c pointers struct


【解决方案1】:

有趣的 pdf。

我的第一个问题是,为什么 **cp 是常数?有必要还是只是 彻底,因此代码编写者不会做任何破坏性的事情 意外?

这是必要的,所以作者不会意外地做任何事情,是的,并且向代码的阅读者传达一些关于指针的性质及其用途的信息。

其次,为什么 cp 是指向指针的指针(双星号?)。这 struct 类在 pdf 的第 12 页上定义。我不明白为什么 它不能是单个指针,因为我们将 self 指针转换为 似乎是一个类指针。

查看new() 的定义(第13 页),其中创建了指针p(与self 传递给delete() 的指针相同):

void * new (const void * _class, ...)
{
    const struct Class * class = _class;
    void * p = calloc(1, class —> size);
    * (const struct Class **) p = class;

因此,“p”被分配空间,然后被取消引用并分配一个指针值(类中的地址;这就像取消引用并分配给一个 int 指针,但我们分配的是一个地址而不是一个 int)。 这意味着 p 中的第一件事是指向其类定义的指针。 但是,p 被分配的空间不仅限于此(它还将保存对象的实例数据)。现在再次考虑delete()

 const struct Class ** cp = self;
 if (self&&*cp&&(*cp)->dtor)

当 cp 被取消引用时,因为它是一个指向指针的指针,它现在是一个指针。指针包含什么?一个地址。什么地址? 指向类定义的指针,它位于 p 所指向的块的开头。

这有点聪明,因为 p 并不是真正的指向指针的指针——它分配了更大的内存块,其中包含特定的对象数据。但是,该块的开头是一个地址(类定义的地址),因此如果将 p 取消引用为指针(通过强制转换或 cp),您就可以访问该定义。因此,类定义只存在于一个地方,但该类的每个实例都包含对该定义的引用。有道理?如果 p 被键入为这样的结构会更清楚:

struct object {
  struct class *class;
    [...]
};

然后你可以使用p->class->dtor() 之类的东西来代替delete() 中的现有代码。然而,这会弄乱大局并使大局复杂化。

第三,如何将一个void指针更改为一个类指针(或 指向类指针的指针)?我觉得这个问题最能说明我的不足 对C的理解。我脑海中想象的是一个空指针 占用一定数量的内存,但必须小于Class 指针,因为一个类里面有很多“东西”。

指针就像一个 int ——它有一个小的、固定的大小来保存一个值。该值是内存地址。当您取消引用指针(通过*->)时,您正在访问的是该地址处的内存。但是由于内存地址都是相同的长度(例如,在 64 位系统上为 8 个字节),因此无论类型如何,指针本身的大小都是相同的。这就是对象指针“p”的神奇之处。重申一下:p 指向的内存块中的第一件事是一个地址,它允许它作为指向指针的指针,当它被取消引用时,你会得到包含类定义的内存块,它与p 中的实例数据是分开的。

【讨论】:

    【解决方案2】:
    1. 在这种情况下,这只是一种预防措施。该函数不应该修改类(实际上,什么都不应该修改),因此强制转换为 const struct Class * 可以确保类更难被无意更改。

    2. 我对这里使用的面向对象的 C 库不是很熟悉,但我怀疑这是一个讨厌的把戏。 self 中的第一个指针可能是对类的引用,因此取消引用 self 将给出指向该类的指针。实际上,self 始终可以被视为struct Class **

      图表可能会有所帮助:

              +--------+
      self -> | *class | -> [Class]
              |  ....  |
              |  ....  |
              +--------+
      
    3. 请记住,所有指针都只是地址。* 指针的类型与指针的大小无关;它们都是 32 位或 64 位宽,具体取决于您的系统,因此您可以随时从一种类型转换为另一种类型。如果您尝试在不进行强制转换的指针类型之间进行转换,编译器会警告您,但 void * 指针始终可以在不进行强制转换的情况下转换为任何类型,因为它们在整个 C 中用于指示“通用”指针。

    *:有一些奇怪的平台不是这样的,不同类型的指针实际上有时是不同的大小。但是,如果您使用其中之一,您就会知道。你很可能不是。

    【讨论】:

      【解决方案3】:
      1. const 用于在代码尝试更改指向对象内的任何内容时导致编译错误。当程序员只打算读取对象而不打算更改它时,这是一项安全功能。

      2. ** 被使用,因为它必须是传递给函数的内容。将其重新声明为不是的东西将是一个严重的编程错误。

      3. 指针只是一个地址。在几乎所有现代 CPU 上,所有地址的大小都相同(32 位或 64 位)。将指针从一种类型更改为另一种类型实际上并不会更改值。它表示将该地址的内容视为不同的数据布局。

      【讨论】:

        猜你喜欢
        • 2021-07-14
        • 1970-01-01
        • 1970-01-01
        • 2017-01-04
        • 2016-08-25
        • 1970-01-01
        • 2016-03-21
        • 2012-03-28
        相关资源
        最近更新 更多