【问题标题】:When do we use structure variable and structure pointer?我们什么时候使用结构变量和结构指针?
【发布时间】:2020-11-08 18:50:54
【问题描述】:

我在C中为链表节点创建了一个结构如下。

struct node
{
    int data;
    struct node *next;
} *START = NULL;

然后当我需要访问结构的属性时,我会创建一个指向结构的指针,如下所示。

struct node *node1;
node1 -> data = 12;
node1 -> next = NULL;

我想知道我们是否可以使用

struct node node1

而不是当前的声明以及它会对程序产生什么变化。

另外,我想知道*START=NULL为什么在结构之外,它的数据类型是什么?

【问题讨论】:

    标签: c pointers linked-list structure


    【解决方案1】:

    您需要为结构分配内存。这里有一个简单的函数,将节点附加到列表的末尾

    
    struct node
    {
        int data;
        struct node *next;
    } *START = NULL;
    
    struct node *append(int data)
    {
        struct node *node1 = malloc(sizeof(*node1));
        struct node *list = START;
        if(node1)
        {
            node1 -> data = data;
            node1 -> next = NULL;
            if(list)
            {
                while(list -> next)
                {
                    list = list -> next;
                }
                list -> next = node1;
            }
            else
            {
                START = node1;
            }
        }
        return node1;
    }
    

    START是指向struct node的类型指针变量。

    【讨论】:

      【解决方案2】:
      struct node *node1;
      node1 -> data = 12;
      node1 -> next = NULL;
      

      首先,上面的代码没有将node1 初始化为有效的内存地址。它只是创建了一个名为node1 的指针,它可以获取尚未正确初始化的struct node 类型变量的地址。

      您需要将代码更改为以下内容。

      struct node *node1 = (struct node*)malloc(sizeof(struct node));
      node1 -> data = 12;
      node1 -> next = NULL;
      

      上面会为struct node类型分配内存,并用分配的内存地址初始化node1

      现在让我们来回答您的问题。

      我想知道我们是否可以使用

      struct node node1

      而不是当前的声明,以及它会在程序中产生什么变化。

      您可以在程序中使用上述声明,但它会在堆栈而不是堆上创建node1。可能这不是您想要的行为,因为在堆栈上创建的变量具有创建它的范围的生命周期。在您的情况下,我假设您要创建一个链接列表(或类似结构),因此即使在发生附加的函数返回之后,您也需要可以访问该列表。

      无论如何,如果您在堆栈上创建了变量,您可以简单地使用. 运算符来访问结构成员。

      struct node node1;
      node1.data = 12;
      node1.next = NULL;
      

      另外,我想知道为什么 *START=NULL 在结构之外以及什么 它的数据类型是什么?

      它只是定义了一个名为START的指针变量,它可以指向struct node并用NULL初始化它。

      【讨论】:

      • 两行代码中的三个问题: 1. 强制转换 malloc 的结果被认为是不好的做法。 2.不检查malloc的结果是严重错误。 2. 在 sizeif 中使用类型而不是对象被认为是一种不好的做法。
      • struct node *node1 = malloc(sizeof(struct node)); 应该是struct node *node1 = malloc(sizeof(*node1)); 为什么?如果您的代码中有许多类似的 sizeof-s 并且您更改了 node1 的类型,您的代码将不稳定。使用类型很容易出错。这里讨论了很多次 - 搜索更详细的解释。
      • 当你每周花一天时间去追查你的方法引起的难以发现的错误时 - 你会改变主意。
      • @P__J__ 我认为您的评论是主观的,而不是一个好的做法或一般来说不是。顺便说一句,很高兴知道不同的看法。
      • @P__J__ 我确实不会因此而否决一个问题。这只是编码风格的问题。两个版本都是正确的,虽然“指针类型”版本确实更安全。
      【解决方案3】:

      [...] 当我需要访问结构的属性时,我创建 一个指向如下结构的指针。

      struct node *node1;
      node1 -> data = 12;
      node1 -> next = NULL;
      

      不完全是。您已将node1 声明为struct node * 类型的变量,但您没有 创建了一个指针,因为您没有为该变量指定一个指向任何结构的值。随后尝试通过该变量的(不确定的)值访问 struct node 因此会产生未定义的行为。更有可能的结果是您的程序崩溃或对其内存中的随机对象进行了意外更改。

      为了能够使用node1 访问您所展示的struct node,您首先需要将其分配为指向一个(或至少指向可以包含一个的内存)。您可以通过将现有struct node 的地址分配给它,或者为struct node 分配足够的内存并将其地址分配给node1 来做到这一点。稍后将详细介绍这些替代方案。

      我想知道我们是否可以使用

      struct node node1
      

      而不是当前的声明以及这会带来哪些变化 程序。

      您绝对可以将node1 声明为struct node 而不是指向一个的指针。在此类声明的范围内,您将通过直接成员访问运算符 (.) 访问其成员,而不是通过间接成员访问运算符 (->):

      node1.data = 12;
      node1.next = NULL;
      

      此外,获取指向struct node 的指针的方法之一是使用地址运算符 (&) 来获取该结构的地址:

      struct node *node_ptr = &node1;
      

      但是,以这种方式声明的 node1 对象的生命周期在控制权从出现声明的最内层块(如果有)中结束时结束,尽管有任何指向它的指针。因此,这通常不是您想要的链表节点。

      对于链表应用程序(以及其他应用程序),通常需要一个对象,其生命周期在您说它应该结束之前不会结束,这可以通过为结构动态分配内存来实现。例如,

      struct node *node_ptr = malloc(sizeof(*node_ptr));
      

      分配的内存保持分配状态,直到您明确地free() 它,无论是在该范围内还是在另一个范围内。无论哪种方式,要通过有效指针访问结构成员,都可以使用间接访问运算符,如您的示例所示:

      node_ptr->data = 42;
      node_ptr->next = NULL;
      

      或者,等效地,首先取消引用指针,然后使用直接成员访问运算符:

      (*node_ptr).data = 42;
      (*node_ptr).next = NULL;
      

      .

      另外,我想知道为什么 *START=NULL 在结构之外以及什么 它的数据类型是?

      你说编写了代码。如果您不知道*START=NULL 部分的意义,那么它在您的代码中做了什么?

      无论如何,它类似于上面的*node_ptr = &node1START 被声明(在文件范围内)为指向 struct node 的指针,并且其初始(指针)值被分配为 NULL,它明确且可测试地不指向任何结构。

      【讨论】:

        【解决方案4】:

        我们什么时候使用结构变量和结构指针?

        取决于用例。创建结构变量时,实际上是在堆栈上分配结构对象。当您使用指针时,您通常在堆内存中分配结构对象并通过指针指向该对象。

        您可以随时调整动态分配的结构对象的大小和解除分配,而静态不能更改大小,并且仅在其范围结束时才被销毁(如果是automatic 结构变量)。

        有关静态分配与动态分配之间区别的更多详细信息,您可以在答案下方的链接中找到。

        您选择哪种方式,取决于您想做什么以及如何去做。

        我想知道我们是否可以使用struct node node1 来代替当前的声明,以及这会对程序产生什么影响。

        这将使node1 成为结构本身的变量;它不再是指向结构对象的指针了。

        除了上面提到的和其他的,对成员的访问会有所不同:

        node1 . data = 12;
        node1 . next = NULL;
        

        我也想知道为什么*START=NULL在结构之外,它的数据类型是什么。

        START 的类型为 struct node *(指向 struct node 的指针)并初始化为 NULL。它的定义在外部,因为它不是成员,而是指向结构对象的指针。


        请注意,您需要分配一个指向struct node 的指针以指向struct node 的对象,但这不是您所做的:

        struct node *node1;
        node1 -> data = 12;
        node1 -> next = NULL;
        

        所以,这会引发未定义的行为。

        为结构分配内存:

        struct node *node1 = calloc (1, sizeof(*node1));
        if (!node1)
        {
            fputs("Failure at memory allocation for node1!", stderr);
            return EXIT_FAILURE;
        }
        node1 -> data = 12;
        node1 -> next = NULL;
        

        关于第 1 点:

        【讨论】:

        • 结构 vs 指向结构的指针不一定是堆栈 vs 的问题。堆。
        • @JohnBollinger 是的,你是对的。但是恕我直言,指向结构的指针更常用于分配动态内存,而不仅仅是指向静态分配的结构。
        • 我不反对,但这无关紧要。这个答案中关于堆栈和堆的部分没有响应 OP 实际提出的问题,并且鉴于 OP 明显的 C 经验水平,我认为这些部分更有可能使问题变得混乱而不是有帮助。
        • @JohnBollinger OP 的问题是“我们什么时候使用结构变量和结构指针?” - 有意使用的一个 主要 区别是关注点在哪里存储,或者更精确的静态与动态分配,从而解释堆栈和堆是有意义的。
        • @JohnBollinger 鉴于此,你会特别改变什么?删除链接或一般提及堆栈和堆?
        猜你喜欢
        • 1970-01-01
        • 2010-10-19
        • 1970-01-01
        • 2021-01-07
        • 2014-01-31
        • 2015-07-13
        • 1970-01-01
        • 2012-04-13
        • 1970-01-01
        相关资源
        最近更新 更多