3

in order to create a linked list(which will contain an attribute of next and previous node),i will be using pointers for the 2 next and previous nodes,yet i was wondering if i could complete the code without using malloc(allocating memory): for example: instead of malloc-ing:

link *const l = (link *)malloc(sizeof(link));

if(l == NULL)
  /* Handle allocation failure. */
  ...

l->data = d;
l->next = list->head;

head = l;

can i simply create a new link variable with the attributes formatted(value,pointer to next and previous link),and simply link the last link in my last link in the chain to this one? my list file is b,for example.

link i;
i.date=d;
getlast(b).next=&i

i appologize ahead for the fact i am new to c,and will be more than glad to receive an honest solution :D

edit: i tried using malloc to solve the matter.i will be glad if anyone could sort out my error in the code,as i can not seem to find it.

#include <stdio.h>
#include <malloc.h>

struct Node{
    int value;
    struct Node * Next;
    struct Node * Previous;


};
typedef struct Node Node;
struct List{
    int Count;
    int Total;
    Node * First;
    Node * Last;
};
typedef struct List List;
List Create();
void Add(List a,int value);
void Remove(List a,Node * b);
List Create()
{
    List a;
    a.Count=0;
    return a;

}

void Add(List a,int value)
{
    Node * b = (Node *)malloc(sizeof(Node));
    if(b==NULL)
        printf("Memory allocation error \n");
    b->value=value;
    if(a.Count==0)
    {
        b->Next=NULL;
        b->Previous=NULL;
        a.First=b;

    }
    else
    {
        b->Next=NULL;
        b->Previous=a.Last;
        a.Last->Next=b;

    }
    ++a.Count;
    a.Total+=value;
    a.Last=b;
    }
void Remove(List a,Node * b)
{
    if(a.Count>1)
    {
        if(a.Last==b)
        {
            b->Previous->Next=NULL;
        }
        else
        {
            b->Previous->Next=b->Next;
            b->Next->Previous=b->Previous;
        }

        }
    free(b);
    }
6
  • Can you? Sure. Whether you want to is debatable, as there are other formidable ways of keeping a "linked" list in automatic storage, and plenty of downsides to doing it as you are. A node array that includes the datum and an index to the next node in the array is such an example). The fundamental purpose of a pointer-based linked list is to provide dynamic expansion, and I'm not entirely convinced you're clear on that. Commented Nov 18, 2013 at 19:01
  • 1
    This always gets posted whenever someone does it, but don't cast the return value of malloc. Commented Nov 18, 2013 at 19:01
  • 3
    The issue in your code is "where does 'i' go when that function returns?" Commented Nov 18, 2013 at 19:02
  • Think about why the traditional method is to use malloc. Does your version address those reasons? Commented Nov 18, 2013 at 19:07
  • i appologize ahead for the ignorance;i do understand the purpose of using pointers.i do not understand what will actually happend if i reuse the i variable:i am creating a node in the memory.after doing so,i am linking the last node in my list to this new node created in the memory.what will happend if i reuse the i variable and why must i use malloc? Commented Nov 18, 2013 at 19:07

4 Answers 4

6

Yes - you can do that.

e.g.

link l1,l2;
l1.next = &l2;
l2.next = NULL;

Is a perfectly fine and valid linked list of 2 nodes. You could also create a bunch of nodes, and link them together based on your needs, e.g. create a linked list of the argv:

 int main(int argc, char *argv[])
       int i;
       link links[100];
       for (i = 0; i < argc && i < 100; i++) {
          //assuming the nodes can hold a char*
           links[i].data = argv[i]; 
           links[i].next = NULL;
           if (i > 0)
              links[i-1].next = &links[i];
        }

There are of course some drawbacks:

  • The number of nodes is determined at compile time in these examples. (in the last example one could malloc a buffer for argc nodes instead of hardcoding 100 though)
  • The lifetime of these nodes are the scope they are declared in, they no longer exists when the scope ends.

So you cannot do something like this:

 void append_link(link *n, char *data)
 {
      link new_link;
      n->next = &new_link;
      new_link.next = NULL;
      new_link.data = data;
 }

That is invalid, since when append_link ends, the new_link is gone. And the passed in n->next now points to a local variable that is invalid. If new_link instead was malloc'ed, it will live beyond this function - and all is ok.

Sign up to request clarification or add additional context in comments.

2 Comments

okay.i edited my question with my new code solution with malloc.could you possibly point out my error,as i cant seem to find it?(it only includes insert and remove so far
@user3005945 Don't do that. Your question is now a completely different one, so you should create a new question for it.
3

Not really.

You could create a variable for each and every node in your list, but what happens when you want another node? Fifty more nodes? These variables also won't hang around after you've left the scope they were defined in, which means you'd either have to make everything global or use static storage and expose a pointer to them. This means that all pointers to them after that scope will be invalid. These are both very ugly solutions.

If you don't understand what I mean by scope, here's a quick example:

int main() { /* Entering function scope. */
    int x = 5;
    { /* Entering block scope. */
        int y = 7;
        printf("%d\n", y);
    } /* Exiting block scope, all variables of this scope are gone. (y) */
    printf("%d %d\n", x, y); /* Won't compile because y doesn't exist here. */
} /* Exiting function scope, all non-static storage variables are gone. (x)

You could also create a global array, thinking that this gets around having a lot of different variables, but if your solution is to implement this using an array, why are you using a linked list and not an array? You've lost the benefits of a linked list by this point.

5 Comments

thanks for your reply,simply to make sure i understood the problem:when i create a variable inside the function,and create a pointer to this variable,i am actually pointing to the variable itself and not to the block of memory in which the variable is located.and that is why when the function is terminated(ended),the variable no longer exists?
Mm, no. You are still pointing to the memory that the variable uses. The problem is that the variable is generally stored on the stack, which is a segment of memory you don't own except for the duration of that block or function. Exactly how it happens is compiler dependent, but you can't make guarantees about that memory outside of that scope--your compiler might decide to reuse that memory!
Here's create-linked-list-no-malloc.c example that hides from user the fact that an array is used as a storage. It is not that unusual to use a custom allocator. Some c++ stl containers are allocator-aware e.g., forward_list<>.
Yes, you can do it, but why bother if you don't have a reason? Arbitrary insertions and deletions are not as simple as you'll need to manually track (or search for) free slots. The benefit of spatial locality diminishes as your list becomes larger and your elements become less ordered--so now you have to periodically reorder your elements. Potentially wasteful if you start with a large list and have more deletions than insertions. Growth is more of a pain. Embedded system with fixed memory and use case? Absolutely. A general PC with dynamic size? Not without a good reason.
I'll amend this answer in a bit to emphasize the "You can but probably shouldn't, unless..." side.
2

There are only two ways in C to create in-memory data structures that don't have a fixed-at-compile-time size:

  • with allocated storage duration, i.e. via malloc.
  • with automatic storage duration, which in terms of implementation, means "on the stack", either using variable-length arrays or recursion (so that you get a new instance at each level of recursion).

The latter (automatic storage) has the property that its lifetime ends when execution of the block where it's declared terminates, so it's difficult to use for long-lived data. There's also typically a bound on the amount of such storage you can obtain, and no way to detect when you've exceeded that bound (typically this results in a crash or memory corruption). So from a practical standpoint, malloc is the only way to make runtime-dynamic-sized data structures.

Note that in cases where your linked list does not need to have dynamic size (i.e. it's of fixed or bounded size) you can use static storage duration for it, too.

1 Comment

okay.i edited my question with my new code solution with malloc.could you possibly point out my error,as i cant seem to find it?(it only includes insert and remove so far
1

Memory for new nodes has to come from somwhere. You can certainly create individual variables and link them manually:

link a, b, c;
...
a.next = &b;
b.next = &c;
c.next = NULL;

As you can imagine, this approach doesn't scale; if you want more than 3 elements in your list, you'd have to allocate more than 3 link variables. Note that the following won't work:

void addToList( link *b )
{
  link new;
  ...
  b->next = &new;
}

because new ceases to exist when the addToList exits, so that pointer is no longer meaningful1.

What you can do is use an array of link as your "heap", and allocate from that array. You'll need to keep track of which elements are available for use; an easy way of doing that is initializing the array so that each a[i] points to a[i+1] (except for the last element, which points to NULL), then have a pointer which points to the first available element. Something like the following:

// You really want your "heap" to have static storage duration
static link a[HEAP_SIZE];

// Initialize the "heap"
for ( size_t i = 0; i < SIZE - 1; i++ )
  a[i].next = &a[i+1];
a[i].next = NULL;

// Set up the freeList pointer; points to the first available element in a
link *freeList = &a[0];  

// Get an element from the "heap"
link *newNode = freeList;
freeList = freeList->next;
newNode->next = NULL;

// Add a node back to the "heap" when you're done with it:
deletedNode->next = freeList;
freeList = deletedNode;

Again, you're limited in how many list nodes you can create, but this way you can create a large enough "heap" to satisfy your requirements.


1. Obviously, the phsyical memory location that new occupied still exists, but it's now free for other processes/threads to use, so the value contained in that address will no longer be what you expect.

4 Comments

okay.i edited my question with my new code solution with malloc.could you possibly point out my error,as i cant seem to find it?(it only includes insert and remove so far)
For the Add and Remove functions, you need to pass a as a pointer to List. Remember that C passes all function parameters by value, so the formal parameter is a different object in memory from the actual parameter; any changes made to the formal parameter won't be reflected in the caller.
okay.also,how does the list in c# function without any mallocing?(for example:pastebin.com/9VU5BAx5 notice the insert) also,i solved the code's problem without passing a as a pointer to the list,but as the actual list
@user3005945: C and C# are different languages; what's true for one isn't necessarily true for the other.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.