7

我正在学习 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 指针可以“转换”为另一种类型的指针,但我不明白如何,因为可能没有足够的内存来执行此操作。

提前致谢

4

3 回答 3

3

有趣的.pdf。

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

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

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

看一下创建new()指针的(第 13 页)的定义(与传递到p的相同的指针):selfdelete()

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的理解不足。我脑海中想象的是一个空指针占用了一定的内存,但它必须小于类指针,因为一个类有很多“东西”在里面。

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

于 2012-05-03T18:33:49.880 回答
2
  1. 在这种情况下,这只是一种预防措施。该函数不应该修改类(实际上,什么都不应该修改),因此强制转换以const struct Class *确保类更难以无意中更改。

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

    图表可能会有所帮助:

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

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

于 2012-05-03T18:22:44.640 回答
0
  1. const如果代码试图更改指向的对象中的任何内容,则用于导致编译错误。当程序员只打算读取对象而不打算更改它时,这是一项安全功能。

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

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

于 2012-05-03T18:19:36.463 回答