【问题标题】:Why can't a struct have a member that is of the same type as itself?为什么结构不能具有与自身相同类型的成员?
【发布时间】:2013-05-20 10:44:20
【问题描述】:

我一直在理解这个结构发生了什么(来自C for Programmers Deitel 的书)。

书上说,“结构不能包含自身的实例。例如,struct employee 类型的变量不能在 struct employee 的定义中声明。但是,可以包含指向 struct employee 的指针。 "

然后给出下面的例子:

struct employee2 {
   char firstName[ 20 ];

   char lastName[ 20 ];

   unsigned int age;

   struct employee2 *ePtr;
};

我不明白这是在做什么,我不明白没有 2 的对 struct employee 的引用。

struct employee2 *ePtr 是如何知道struct employee 的,或者我是不是在这里?

【问题讨论】:

  • 也许获取结构或结构元素地址比访问已经拥有地址的元素慢。
  • 您引用的代码中没有struct employee;只有struct employee2
  • @jamesdlin 这本书没有引用struct employee 的代码。这就是我没有包含它的原因。
  • 这是重复的,但我的答案与那里的任何答案都不同,OP 表示它很有帮助 - 所以问题应该合并而不是关闭。 stackoverflow.com/questions/588623/…
  • @nkon:你问“struct employee2 *ePtr 怎么知道struct employee”。它不知道也不需要知道; struct employee 没有以任何方式参与。 struct employee2 是。

标签: c struct


【解决方案1】:

一个更有意义的例子可能是

struct employee2* manager;

请注意,删除* 意味着 C 编译器必须为顶级员工布置 44 个(左右)字节,然后为下一个内部员工布置另外 44 个字节,然后为下一个下一个员工布置 44 个字节内部员工,然后 44 为下一个下一个内部员工......等等。不用说这是一个编译错误。

此外,这种不可能的结构将迫使他们都成为 distinct 员工,并要求在创建任何员工时创建所有经理,这些经理必须不为空且不同. 这意味着您不能有 CEO,而使用指针时,CEO 的经理可能是 NULL 或她自己,具体取决于您的实施。这也使得在不从员工系统中删除记录(即解雇员工)并重新创建(雇用)记录的情况下更换经理是不可能的,这还需要撤销建筑物访问权、计算机访问权等。我的意思是,没有指针是对现实世界中正在发生的事情进行建模的一种非常非常糟糕的方式。

但是,C 编译器可以为员工布局 44 个字节,然后为员工经理的 地址 布局 4 个字节,这反过来又指向 44+4 个字节,如果 它不为空。请注意,这些不一定是不同的字节 - 也许员工是她自己的经理(您的业务逻辑应该禁止这样做,但是嘿,C 关心什么)。

一个低级的例子是一个链表,它更像这样:

typedef struct {
    int data;
    node* next;
} node;

但是,同样的想法。除非您准备好立即创建所有无限不同的节点,否则这是行不通的。链表将以NULL 值结尾,可以用指针表示,但不能为不能为空的结构,因为它需要占用内存。

无论如何,指针是一个结构引用另一个结构而无需再次物理布局内存的方式。 C 是一种低级语言,但如果您学会从编译器的角度思考,一些高级概念将是有意义的。例如,删除* 也意味着员工“拥有”她的经理。从现实世界的角度来看,这没有意义,从内存管理的角度来看也没有意义。 (虽然,在某些意义上,父母可以拥有孩子......这不是一个完美的类比。)

【讨论】:

  • 这是一个很棒的解释和回应!
【解决方案2】:

当您了解不完整类型的 C 概念后,这里的魔力就会变得清晰。 结构只能包含完整的类型,即编译器知道其大小的类型。 当编译器看到

struct foo {

它知道会有一个带有 foo 标签的结构体;这种类型 (struct foo) 目前还不完整。直到看到匹配的},它才完成。

然而,这就是魔法,指向不完整类型的指针是完整类型,因为任何指针的大小都是已知的——无论它指向什么类型。所以在上面三个token之后,声明一个struct成员就可以了

  struct foo *ptr_to_struct_foo;

struct foo 内和匹配的} 之前。

另外一个常用的不完全类型是void--这个类型甚至不能完成,这就是为什么C不允许你声明

void foo;

但是,声明指向这种类型的指针是完全可以的:

void *foo;

当然,通过 ptr-to-void 间接是不允许的。为什么?现在你知道答案了:因为这会产生一个不完整的类型。

【讨论】:

  • 所以我可以在技术上做到这一点:struct foo { 然后创建一个指向它的指针,编译器不会抱怨?
  • 不,您必须提供一个语法有效的声明,其中至少包含一个成员和一个结束 }。但是,您可以在这些标记之后立即使用struct foo * 来声明结构的成员。
【解决方案3】:

你不能把一个完整的结构放在自己里面,因为它是无限递归的。

但是你可以把一个结构的另一个实例的地址放在一个结构内;这就是指针的含义...SomeStruct* 大小的地址始终相同,因此编译器知道要为结构的一个实例创建多少内存。

【讨论】:

    【解决方案4】:

    这将导致大小无限。考虑结构:

    struct my_struct {
      int x;
      struct my_struct y;
    }
    

    这会有一个大小:

    sizeof(struct my_struct) >= sizeof(int) + sizeof(struct my_struct);
    

    这显然是无法解决的。

    但是,指向结构的指针不会有这个问题。

    struct my_struct2 {
      int x;
      struct my_struct2* y;
    }
    

    因为现在的尺寸是可能的。

    sizeof(struct my_struct2) >= sizeof(int) + sizeof(struct my_struct2*);
    

    你应该明白两者的区别

    sizeof(struct my_struct2)
    and
    sizeof(struct my_struct2*)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多