【问题标题】:unable to modify global variable in c无法在c中修改全局变量
【发布时间】:2013-10-06 21:41:45
【问题描述】:

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

我想对列表执行两个操作 - 填充它,然后打印它。

为了填充列表,我使用两个参数调用函数insert_nodehead,以及我要插入的整数。

问题是我需要函数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;

}

【问题讨论】:

  • 如果您使用 GCC,请使用-Wshadow 以及其他警告选项(至少为-Wall)进行编译。它会让您知道函数的head 参数意味着这些函数无法访问全局变量head(局部变量隐藏了全局变量)。 (在 C++ 中,有一个作用域运算符来消除歧义(head::head),但在 C 中没有。)
  • 您将head pointer 传递给insert 函数的每个值,而它应该通过引用传递。你应该使用insert_node ( struct node ** head);
  • head 处理方式的变化无关,您需要为新节点动态分配内存(malloc() 和错误检查)。目前,您将数据存储在局部变量中,然后将该局部变量的地址添加到列表中。这不是幸福的秘诀。您的案例 3、4、5 应该更简单地处理。由于您按排序顺序维护列表,因此当下一个指针为空或下一个节点中的数据大于或等于您要添加的节点时,您需要停止扫描。您不应该从头开始重新扫描列表。
  • 乔纳森,你对“(不)幸福的秘诀”是正确的。修复影子变量名称后,我一直只将最后一个元素放入列表中。我花了几个小时调试 - 结果发现,每次调用 insert_node 函数时,都会在同一地址创建局部变量 N。由于head 指向N,这意味着head 将始终指向最新的N。这个问题是使用 malloc 的原因吗?
  • 乔纳森,我真的很感谢你的 cmets。我将研究改进案例 3-5。还有几个问题:应该进行哪些错误检查?关于 gcc 选项 - 还有什么其他好的选项可以使用(对于像我这样的初学者)?

标签: c linked-list global-variables


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

声明

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

用法

insert_node(&head, 5);

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

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

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

【讨论】:

    【解决方案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
    

    【讨论】:

      【解决方案3】:

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

      去掉同名参数,问题就消失了。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-01
        • 2018-12-05
        • 1970-01-01
        • 2012-03-02
        相关资源
        最近更新 更多