1

我正在学习 C,在这个程序中我试图实现一个简单的链表。列表的每个节点都包含一个整数和一个指向下一个节点的指针。指针head指向列表中的第一个节点,但最初列表是空的,所以我初始化了head = NULL.

我想在列表上做两个操作 - 填充它,然后打印它。

为了填充列表,我insert_node使用两个参数调用函数:head和要插入的整数。

问题是我需要该函数insert_node来更改值head(因此它指向更新的列表,而不是 NULL)。我不知道该怎么做,所以我做head了一个全局变量,我试图改变它的值。由于某种原因,即使head函数内部的值发生了变化insert_node,当我再次调用该函数时,head 的值仍然为 NULL。

问题:

  1. 为什么全局变量值不会全局更改?

  2. 我知道使用全局变量不是一个好习惯,那么如何正确更新指向列表的指针?我在考虑让insert_node函数实际上返回一个指向列表的指针,这是一个好方法吗?

#include<stdio.h>
#include<stdlib.h>


struct node {

  int data;
  struct node *link;
};

void insert_node(struct node *head, int n); 
void print_list(struct node *head);

struct node *head = NULL;

main() {

  int i;

  for(i=1; i<5; i++) 

      insert_node(head, i*i);

  print_list(head);

}

void print_list(struct node *head) {

  if(head == NULL) 

      return;

  else {

      printf("%i ", head->data);  
      print_list(head->link);
  }

  return;
}


void insert_node(struct node *head, int n) {

  struct node N = {n, NULL};
  struct node *next, *prev;
  int prev_data = 0;

  //case one: list is empty - point head to N, and set N.link to NULL

  if(head == NULL) 

      head = &N;

  //case two: n is less than first element in the list:

  else if(n < head->data) {

      N.link = head;
      head = &N;
  }


  else {

      next = head;

  //case three: N.data is equal to existing element, do nothing:

      while(next != NULL) {

          if(n == next->data) {

              printf("this element already exists.\n\n");
              return; 
          }
          prev = next;          //save the current element
          next = next->link;    //look at the next element
      }

  //case four: N.data is greater than last element:

      if(n > prev->data) {

          prev->link = &N;
          return;
      }

  //case five: N.data is in between list elements:

      next = head;

      while(next != NULL) {

          prev_data = next->data;   //save the current element
          prev = next;              //save pointer to current element
          next = next->link;        //look at the next element

          if((n > prev_data) && (n < next->data)) {

              prev->link = &N;
              N.link = next;
              return;
          }
      }
  }

  return;

}
4

3 回答 3

5
  1. 因为您将 globalhead 按值传递给 function insert_node()
    然后函数insert_node()生成局部变量(顺便说一下,它的名称head可能会让您感到困惑,因为它是本地的而不是全局的)。修改本地head和那些更改在全局变量中不可见head。这就是所谓的阴影(具有相同名称但在本地范围内的变量与任何其他具有相同名称的变量不同)。
  2. 将头地址传递给函数,并使函数参数指针指向结构指针。

宣言

void insert_node(struct node **ptr_to_head, int n);

用法

insert_node(&head, 5);

ptr_to_head现在您可以通过在insert_node函数中取消引用来修改 head :

(*ptr_to_head)=&new_node;
     ^            ^
     |            |
   head       =  value returned by malloc 

是的,您可以head从 insert_node 函数返回,但不要忘记head在 main 函数中进行赋值。

于 2013-10-06T21:45:17.587 回答
2

在评论中,我说插入代码不应该需要像你一样多的情况。这是证据。它包括释放分配列表的代码。请注意,特殊情况较少(只有三种:重复条目、在头部插入、在其他地方插入)。

#include <stdio.h>
#include <stdlib.h>

struct node
{
    int data;
    struct node *link;
};

void insert_node(struct node **head, int n);
void print_list(struct node *head);
void free_list(struct node **phead);
void test_insert(struct node **head, int n);

int main(void)
{
    struct node *head = NULL;
    free_list(&head);

    for (int i = 1; i < 5; i++)
        test_insert(&head, i*i);
    test_insert(&head, 0);
    test_insert(&head, 7);
    for (int i = 1; i < 6; i++)
        test_insert(&head, i*i - 3);
    test_insert(&head, 7);
    test_insert(&head, 0);

    free_list(&head);
    return 0;
}

void insert_node(struct node **phead, int n)
{
    struct node *node = malloc(sizeof(*node));

    if (node == NULL)
    {
        fprintf(stderr, "Failed to allocate node for %d\n", n);
        exit(1);
    }

    node->data = n;

    struct node *head = *phead;
    struct node *next = head;
    struct node *prev = NULL;

    while (next != NULL && n > next->data)
    {
        prev = next;
        next = next->link;
    }

    if (next != NULL && n == next->data)
        free(node);
    else
    {
        node->link = next;
        if (prev == NULL)
            *phead = node;
        else
            prev->link = node;
    }
}

void test_insert(struct node **head, int n)
{
    printf("%2d:", n);
    insert_node(head, n);
    print_list(*head);
}

void print_list(struct node *head)
{
    while (head != NULL)
    {
        printf(" %2i", head->data);
        head = head->link;
    }
    putchar('\n');
}

void free_list(struct node **phead)
{
    struct node *head = *phead;
    while (head != NULL)
    {
        struct node *next = head->link;
        free(head);
        head = next;
    }
    *phead = 0;
}

示例输出:

冒号左边的值是“插入”值;右边的值是结果列表。

 1:  1
 4:  1  4
 9:  1  4  9
16:  1  4  9 16
 0:  0  1  4  9 16
 7:  0  1  4  7  9 16
-2: -2  0  1  4  7  9 16
 1: -2  0  1  4  7  9 16
 6: -2  0  1  4  6  7  9 16
13: -2  0  1  4  6  7  9 13 16
22: -2  0  1  4  6  7  9 13 16 22
 7: -2  0  1  4  6  7  9 13 16 22
 0: -2  0  1  4  6  7  9 13 16 22
于 2013-10-15T06:21:31.920 回答
1

您添加了一个名为 head 的全局变量,但您忘记删除函数 insert_node、print_list 等具有相同名称的参数。本地优先于全局,因此您的分配分配给本地而不是全局。

删除具有相同名称的参数,问题就会消失。

我不宽恕使用全局变量:)

于 2013-10-06T22:08:09.833 回答