unable to modify global variable in c

好久不见. 提交于 2019-12-11 07:50:35

问题


I'm learning C, and in this program I'm trying to implement a simple linked list. Each node of the list contains an integer, and a pointer to the next node. Pointer head points to the first node in the list, but initially the list is empty, so I initialized head = NULL.

I want to do two operations on the list - populate it, and print it.

To populate the list, I'm calling function insert_node with two arguments: head, and the integer I want to insert.

The problem is I need the function insert_node to change the value of head (so it points to the updated list, instead of NULL). I'm not sure how to do that, so I made head a global variable, and I'm trying to change its value. For some reason, even though the value of head is changed inside the function insert_node, when I call the function again, head still has value of NULL.

Questions:

  1. Why is global variable value is not changed globally?

  2. I'm aware that using global variables is not a good practice, so how can I properly update the pointer to the list? I was thinking about having insert_node function actually return a pointer to the list, is this a good way?

#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;

}

回答1:


  1. Because you pass global head by value to function insert_node().
    Then function insert_node() makes local variable (which, by the way, also has the name head which might confuse you, because it's local and not global). Modifies that local head and those changes are not visible in global variable head. It's so called shadowing (variable with the same name but within local scope is distinct from any other variable with the same name).
  2. Pass address of head to function and make function parameter pointer to pointer to structure.

Declaration

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

Usage

insert_node(&head, 5);

Now you can modify head by dereferencing ptr_to_head in your insert_node function:

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

And yes, you can return head from insert_node function, but don't forget to make an assignment to head in main function.




回答2:


You added a global variable named head but you forgot to remove the parameters on the functions insert_node, print_list, etc. that has the same name. The local takes precedence over the global, so your assignments are assigning to the local not the global.

Remove the parameters with the same name and the issue will go away.

I'm not condoning the usage of globals though :)




回答3:


In a comment, I said that the insert code should not need as many cases as you have. Here's the proof. It includes code to free the allocated list. Notice that there are fewer special cases (just three: duplicate entry, insert at head, insert somewhere else).

#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;
}

Example output:

The value to the left of the colon is the value 'inserted'; the value to the right is the resulting list.

 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


来源:https://stackoverflow.com/questions/19214293/unable-to-modify-global-variable-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!