Why use dynamic memory allocation(i.e. malloc()) when implementing linked list in c?

跟風遠走 提交于 2021-02-15 05:34:57

问题


Okay this question may sound stupid to the amateur programmers . But seriously this is bothering me and a solemn answer to this doubt of mine is welcomed. I have just started to take my first ever course in data structures. And what is bothering me is this:

Assuming C is used,

//Implementing a node

struct Node
{
     int data;
     struct *Node;
};

Now while creating a node why do we use the dynamic memory allocation technique where we use malloc(). Can't we just create a variable of type ' Struct Node '. i.e. something like:

struct Node N1;
 //First node - actually second where !st Node is assumed to be Head.

struct Node *Head = &N1;
struct Node N2;
N2.(*Node) = &N1;

Well some parts of my code may be incorrect because I am only a beginner and not well versed with C. But by know you may have understood what I basically mean. Why don't we create variables of type Node of an Array of type Node to allocate memory t new nodes why get into the complexity of dynamic memory allocation?


回答1:


First off, you have an error in how you declare your struct. struct * by itself does not denote a type. You have to give the full type name:

struct Node
{
     int data;
     struct Node *Node;
};

You can certainly use local variables as above to make a linked list, however that limits you to a fixed number of list elements, i.e. the ones you explicitly declare. That would also mean you can't create a list in a function because those variables would go out of scope.

For example, if you did this:

struct Node *getList()
{
    struct Node head, node1, node2, node3;
    head.Node = &node1;
    node1.Node = &node2;
    node2.Node = &node3;
    node3.Node = NULL;
    return &head;
}

Your list would be restricted to 4 elements. What of you needed thousands of them? Also, by returning the address of local variables, they go out of scope when the function returns and thus accessing them results in undefined behavior.

By dynamically allocating each node, you're only limited by your available memory.

Here's an example using dynamic memory allocation:

struct Node *getList()
{
    struct Node *head, *current;
    head = NULL;
    current = NULL;

    // open file
    while (/* file has data */) {
        int data = /* read data from file */
        if (head == NULL) {      // list is empty, so create head node
            head = malloc(sizeof(struct Node *));
            current = head;
        } else {                 // create new element at end of list
            current->next = malloc(sizeof(struct Node *));
            current = current->next;
        }
        current->data = data;
        current->Node = NULL;
    }
    // close file
    return head;
}

This is psedo-code that doesn't go into the details of reading the relevant data, but you can see how you can create a list of arbitrary size that exists for the lifetime of the program.




回答2:


If these variables are local, defined inside a function's scope (i.e. stored on the stack), you shouldn't do this, because accessing them after leaving their scope will result in undefined behavior (their contents will likely be overwritten as you call other functions). In fact, any time you return a pointer to a local, stack based variable from your function, you are doing the wrong thing. Given the nature of C, this is problematic since nothing will warn you you are doing something wrong, and it will only fail later when you try to access this area again.

On the other hand, if they are declared as global variables (outside any other function), then you are simply limited by the number of variables declared that way.

You can potentially declare many variables, but keeping track of which one is "free" for use will be painful. Sure, you can even go a step further and say you will have a global preallocated array of nodes to prevent using malloc, but as you are doing all this you are only getting closer to writing your own version of malloc, instead of sticking to the existing, dynamic one.

Additionally, all preallocated space is wasted if you don't use it, and you have no way of dynamically growing your list in runtime (hence the name dynamic allocation).




回答3:


Here is some good reasons to use dynamic memory

  1. When you declare node struct Node N1;this node will store on stack memory. After scope of the node that will get destroy auto.But in case of dynamic you have handle to free the memory when you done.

  2. When you have some memory limitation.

  3. When you don't know the size of array then dynamic memory allocation will help you.




回答4:


One issue could be that you cannot use another function to add a new node to your list.

Remember that automatic variables - like the ones created by struct Node node100; - have scope only inside the function in which they are defined. So when you do something like this:

int main()
{
    struct Node *head;
    /* Some code there you build list as:
       head ---> node1 ---> node2 --> .. ---> node99 
    */

    /* Add a new node using add_node function */
    add_node(head, 555);

    /* Access the last node*/
}

void add_node(struct Node *head, int val)
{
     /* Create new node WITHOUT using malloc */
     struct Node new_node;
     new_node.data = val;

     /* add this node to end of the list */
     /* code to add this node to the end of list */
     /* last_element_of_list.next = &new_node*/

     return;
}

Now you think that you have added a new node to the end of the list. But, unfortunately, its lifetime ends as soon as the add_node function returns. And when you try to access that last node in your main function your program crashes.

So, to avoid this situation you will have put all your code in one single function - so that the lifetime of those nodes do not end.

Having all your code in ONE function is bad practice and will lead to many difficulties.

This was one situation that asks for a dynamic memory allocation, because, a node allocated with malloc will be in scope untill it is freed using free, and you can put code that do different things in different functions, which is a good practice.




回答5:


You don't have to use dynamic memory to create a linked list, although you definitely don't want to create separate variables for each node. If you want to store up to N items, then you'd need to declare N distinct variables, which becomes a real pain as N gets large. The whole idea behind using a linked list is that it can grow or shrink as necessary; it's a dynamic data structure, so even if you don't use malloc and free, you're going to wind up doing something very similar.

For example, you can create an array of nodes at file scope like so:

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

/**
 *  use the static keyword to keep the names from being visible 
 *  to other translation units
 */
static struct node store[N];  /* our "heap" */
static struct node *avail;    /* will point to first available node in store */

You the initialize the array so each element points to the next, with the last element pointing to NULL:

void initAvail( void )
{
  for ( size_t i = 0; i < N - 1; i++ )
    store[i].next = &store[i + 1];
  store[N - 1].next = NULL;
  avail = store;
}

To allocate a node for your list, we grab the node avail points to and update avail to point to the next available node (if avail is NULL, then there are no more available nodes).

struct node *getNewNode( void )
{
  struct node *newNode = NULL;

  if ( avail ) /* if the available list isn't empty */
  {
    newNode = avail;       /* grab first available node */
    avail = avail->next;   /* set avail to point to next available node */
    newNode->next = NULL;  /* sever newNode from available list, */
  }                        /* which we do *after* we update avail */
                           /* work it out on paper to understand why */
  return newNode;
}

When you're done with a node, add it back to the head of the available list:

void freeNode( struct node *n )
{
  n->next = avail;
  avail = n;
}

We're not using dynamic memory in the sense that we aren't calling mallic or free; however, we've pretty much recapitulated dynamic memory functionality, with the additional limitation that our "heap" has a fixed upper size.

Note that some embedded systems don't have a heap as such, so you'd have to do something like this to implement a list on such systems.



来源:https://stackoverflow.com/questions/38803949/why-use-dynamic-memory-allocationi-e-malloc-when-implementing-linked-list-i

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