I have created a single linked list. Everything works fine.
I just want to know if I have done anything potentially dangerous in my code. The code snippets I am concern
Is there any reason you call exit(0) from clean_up function? I think this is potential dangerous, since you don't give a chance to the user to finish program correctly.
As well I would suggest you to use data encapsulation when you building up you data structure:
typedef struct
{
int product_code;
char product_name[128];
int product_cost;
list_node *next;
} list_node;
typedef struct
{
list_node* head;
list_node* tail;
list_node* current;
int size;
} list;
Also it's a good practice to use trail dummy node at the head of your list to make your code more generic.
From pop()
if(head->product_code == code)
{
temp = head;
head = head->next;
free(temp);
// Finished Deleting product
return;
}
In the case of there only being one item, 'head' and 'tail' would be pointing to the same node. However, if you pop this one item, 'head' will be adjusted but 'tail' will still be pointing to the free'd node. This will leave a bad pointer, which may cause your computer to explode.
Addendum: Similarly, 'new_product' will be dangling if you ever pop the last node that was pushed, and clean_up() will leave the 'tail' pointer dangling as well. Even if the code sample provided will never dereference these after they're free'd, dangling pointers in C code should always be treated as "potentially dangerous".
strncpy(new_product->product_name, name, sizeof(new_product->product_name));
if the string is longer than the size you have it won't be terminated correctly.
It looks like you're on the right track, but there are issues. I would remove the global variables, and instead have a list_t struct (containing head and tail) that you pass into functions. As others have noted, you may also want to make the list generic by using (e.g.) a node_t type and void* data pointer.
Generally push and pop are used to refer to adding or removing an item at the beginning, not an arbitrary location (as you do); this is just a question of naming.
If you had product_name char *product_name instead, that would allow you to remove the length limitation as well as the need for strncpy. You would just have the caller allocate the string, and then free it in clean_up.
You could consider using a enum to improve your menu's readability. For "Check if this is in the first node - deleting from head" (same for tail), you should just compare head to product, not compare the codes.
After "tail = previous", you should set tail->next to NULL.
I see no reason why new_product
should be global and every reason why it should not be.
Agree with the issues raised by goldPseudo and thaggie/Steven.
In push()
, replace strncpy()
with strlcpy()
to ensure the destination string is always NUL terminated.
In cleanup()
, I'd suggest that you remove the exit(0);
statement -- you don't need it. Exiting a programme from within a subroutine is generally not the best thing to do.
You should take away one lesson from creating your first singly linked list, and that is, singly linked lists are generally not very useful in the real world because:
pop()
subroutine.You should now attempt to write your first doubly linked list. While doubly linked lists are more complex to implement, they are easier to manipulate (especially when deleting an element) than singly linked lists.