Single linked list - c

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 concerned about is my push, pop, and clean-up. The parts of the code is just for user interaction so not really important (I posted anyway so that it was more clear in what I was doing). Just the linked list application.
Many thanks for any suggestions, as this is my fist attempt.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct product_data product_data_t;
struct product_data
{
int product_code;
char product_name[128];
int product_cost;
product_data_t *next;
};
static product_data_t *head = NULL;
static product_data_t *tail = NULL;
static product_data_t *new_product = NULL;
// Push a product on to the list.
void push(int code, char name[], int cost);
// Pop (delete) a product from the list.
void pop(int code);
// Display all product in the list.
void display_list();
// Delete all memory allocated on the list
void clean_up();
// Display menu
void menu();
int main(void)
{
menu();
getchar();
return 0;
}
void push(int code, char name[], int cost)
{
// Allocate memory for the new product
new_product = calloc(1, sizeof(product_data_t));
if(!new_product)
{
fprintf(stderr, "Cannot allocated memory");
exit(1);
}
/* Populate new products elements fields */
new_product->product_code = code;
strncpy(new_product->product_name, name, sizeof(new_product->product_name));
new_product->product_cost = cost;
new_product->next = NULL;
// Set the head and tail of the linked list
if(head == NULL)
{
// First and only product
head = new_product;
}
else
{
tail->next = new_product;
}
tail = new_product;
}
// Find the product by code and delete
void pop(int code)
{
product_data_t *product = head;
product_data_t *temp = NULL;
product_data_t *previous = head;
int found = 0; // 0 - Not Found, 1 - Found
if(!head)
{
puts("The list is empty");
return;
}
while(product)
{
if(product->product_code == code)
{
found = 1; // Found
// Check if this is in the first node - deleting from head
if(head->product_code == code)
{
temp = head;
head = head->next;
free(temp);
// Finished Deleting product
return;
}
// Check if this is the end node - deleting from the tail
if(tail->product_code == code)
{
temp = tail;
tail = previous;
free(temp);
// Finished deleting product
return;
}
// delete from list if not a head or tail
temp = product;
previous->next = product->next;
free(temp);
// Finished deleting product
return;
}
// Get the address of the previous pointer.
previous = product;
product = product->next;
}
if(!found)
{
printf("code [ %d ] was not found\n", code);
}
// Set all to null after finished with them
product = NULL;
temp = NULL;
previous = NULL;
}
// Traverse the linked list
void display_list()
{
// Start at the beginning
product_data_t *product = head;
while(product)
{
printf("===================================\n");
printf("Product code: \t\t%d\n", product->product_code);
printf("Product name: \t\t%s\n", product->product_name);
printf("product cost (USD): \t%d\n", product->product_cost);
printf("===================================\n\n");
// Point to the next product
product = product->next;
}
// Finished set to null
product = NULL;
}
// Release all resources
void clean_up()
{
product_data_t *temp = NULL;
while(head)
{
temp = head;
head = head->next;
free(temp);
}
head = NULL;
temp = NULL;
// End program - goodbye
exit(0);
}
void menu()
{
int choice = 0, code = 0, cost = 0;
char name[128] = {0};
do
{
fflush(stdin); // Flush the input buffer
puts("========= Welecome to linked list ===============");
puts("[1] Add new product to the list");
puts("[2] Delete a product from the list");
puts("[3] Display all products");
puts("[4] Exit and clean up");
printf("Enter your choice: ");
scanf("%d", &choice);
switch(choice)
{
case 1:
printf("Enter product code: ");
scanf("%d", &code);
printf("Enter cost: ");
scanf("%d", &cost);
printf("Enter name: ");
scanf("%s", name);
push(code, name, cost);
break;
case 2:
printf("Enter product code: ");
scanf("%d", &code);
pop(code);
break;
case 3:
display_list();
break;
case 4:
clean_up();
break;
default:
puts("Incorrect choice");
break;
}
}while(choice != 4);
}

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.

I see no reason why new_product should be global and every reason why it should not be.

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.

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:
They're too hard to manipulate. Just look at the complexity of your pop() subroutine.
Relatively slow because you have to start at the beginning of the list each time you want to retrieve an element from the list.
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.

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.

Following normal naming convensions, push and pop are related to stacks - i.e. push() should add an item to the top of the stack (you add to the tail of the list, which is fine!), and pop() should return and remove the item from the top of the stack (you search for a named item anywhere in the list and remove it.)
Function names aside, I would suggest a more generic (abstract) implementation of the list, where the content of a node is a pointer to arbitrary data (which in your special case will later be a product_data). This way your linked list can be re-used for any content, and is easier to debug, read and to maintain.
It would also be a better idea not to have stuff global, but rather permit multiple instances of a list. The normal C way is to keep the data in a struct, and then to pass an instance as first argument to each function.

Related

access to data allocated by pointer in a structure issue

I'm working on code that using structures and Linked list.
Please help me to understand how can I print any added point that is not in the head of the list created.
Any attempt was a failure.
*Issue on the second print call.
Is my only option is to do head = head->next in order to get the next variables?
Structures:
typedef struct
{
int x;
int y;
}point;
typedef struct {
point *p;
struct Item *next;
}Item;
Main:
void main()
{
Item *head = (Item*)malloc(sizeof(Item)); //head of co-list
if (!head) { //allocation check
printf("Allocation failed (head)\n");
exit(1);
}
head = addBegin(head);
printf("head point: (%d,%d)\n",head->p->x,head->p->y);
system("pause");
head = addBegin(head);
**printf("head second point: (%d,%d)\n",head->next->p->x,head->next->p->y);**
system("pause");
free(head->p);
free(head);
}
The function:
Item * addBegin(Item *head)
{
Item *tmp = (Item*)
malloc(sizeof(Item));
if (tmp) {
tmp->p = (point*)malloc(sizeof(point));
printf("Enter x's point: ");
scanf(" %d", &tmp->p->x);
printf("Enter y's point: ");
scanf(" %d", &tmp->p->y);
tmp->next = head;
return tmp;
}
else{ //memory allocation failed
printf("allocation failed (new head)\n");
exit(2);
return head;
}
You would want to walk the list, with code similar to:
for ( Item* current = &head;
current != NULL;
current = current->next ) {
do_stuff_with(current->p);
}
This stops when you reach the end of the list: that is, when you have processed the last node containing data, whose next pointer is NULL, and updated current to NULL.
On a side note, you want to tweak the definition of Item slightly, to:
typedef struct Item {
point *p;
struct Item *next;
} Item;
Some compilers, including GCC, will realize that Item.next has the same type, and stop giving you spurious warnings about anonymous structs and incomplete types.
it might also pay to decide whether you want to use Item and Point, or item and point. Inconsistent capitalization is confusing.

Can't seem to ever get the print function in linked list in c

cant print the linked list, it gets stuck in an infinite loop cant understand where i'm going wrong.
#include<stdio.h>
#include<stdlib.h>
typedef struct node{
int data;
struct node *next;
}node;
node *head = NULL;
void print(node *head){
node *temp = head;
while(temp!=NULL);
{
printf("%d => ",temp->data);
temp = temp->next;
}
printf("NULL");
}
node *clist(int n){
node *temp = NULL;
node *p = NULL;
int i;
for(i=0;i<n;i++)
{
temp = (node*)malloc(sizeof(node));
printf("Enter the elements of the list.\n");
scanf("%d",&temp->data);
}
if(head!=NULL)
{
while(p->next!=NULL)
p=p->next; //shifting p here node by node
p->next = temp; //last node which was just created
}
else
{
head = temp;
}
return head;
}
node *binsert(int x){
node *temp = NULL;
node *p = NULL;
temp = (node*)malloc(sizeof(node));
if(head!=NULL)
{
temp->next = head;
temp = head;
}
else
{
p = head = temp;
}
return head;
}
int main ()
{
int a, s, i, n,f;
printf("Choose an option : \n1.Create a list.\n2.Exit.\n");
scanf("%d",&s);
switch(s)
{
case 1:
printf("Very Well! Input the number of nodes\n");
scanf("%d",&n);
head = clist(n);
printf("Link List created successfully.\n");
print(head);
break;
default:
exit (0);
}
printf("Choose the operation you want to perform on the linked list:\n1.Add an element to the beginning.\n2.Add an element to the end.\n3.Add an element at a a particular position.\n");
scanf("%d",&a);
switch(a)
{
case 1:
printf("Enter the element you want to insert at the beginning.\n");
scanf("%d",&f);
binsert(f);
printf("Inserted Successfully.\n");
print(head);
break;
case 2:
printf("Error E162B");
}
return 0;
}
I tried changing the head to a global variable. re-wrote the code 7 times. please help.
First:
for(i=0;i<n;i++)
{
temp = (node*)malloc(sizeof(node));
printf("Enter the elements of the list.\n");
scanf("%d",&temp->data);
}
This loop repeats several times so that the user can enter the elements of the list. But it never adds any of the nodes to the linked list. Each iteration of the loop changes temp to point to a newly-allocated node but doesn't add the nodes to the linked list.
Second:
while(temp!=NULL);
This is an endless loop right here. If temp is not NULL, it's not changed anywhere in the loop, so it will never become NULL. You don't want that semicolon there.
Third:
node *head = NULL;
void print(node *head){
node *temp = head;
Don't do this to yourself. You have a global variable called head and you have a parameter called head. When you do node *temp = head; how obvious is it which head that's referring to. Don't give two variables the same name if their scope overlaps.
Fourth:
if(head!=NULL)
{
temp->next = head;
temp = head; // **** here
}
else
{
p = head = temp;
}
return head;
What's the point of temp = head;? The value of temp isn't accessed anywhere. So that can't be right.
Lastly:
I think you didn't ask the right question though. You asked us to explain where you're going wrong, and I've done that. But I don't think that's what you actually need. Maybe you need help developing the algorithm to solve the problem? Perhaps ask a new question describing what you think the algorithm should be (just in words, no need to use code) and ask for help fixing the algorithm.
And, to be blunt, the number of mistakes suggests that you are attempting a task that is significantly beyond your knowledge. This is frustrating and not a good way to learn programming. You should seriously consider attempting simpler tasks first.
You may have stripped it out to keep the question simple, but your real code should contain lots of extra checking and logging to help you understand it. Log when you enter a function. Log each decision the function makes. Log what functions return. That will help you determine where the program's actual operation is deviating from you think it should be doing. Or, if you prefer, learn to use your platform's debugging facilities. That way, you won't be making random changes and hoping that they make everything work but will instead know where the code is first going awry and be able to fix one thing at a time confident that you are not breaking things that were working.

Inserting Node Linked List C

I am trying to create a linked list which stores name and age of a student.
I am having trouble with insertion.
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
typedef struct node{
char Name[50];
int studentAge;
struct node* next;
}MyNode;
this is how i defined my Struct which constains the requried data and a pointer 'next' which points to the next node.
Below is my insertion function
so in the first if condition i am saying if there isnt a head ie head = NULL then create memory space for the head using malloc.. after this i copy all the data into the head node and making sure that the next of head points to null.
In the second condition i am saying if there is a head ie Head ! = NULL
then traverse the list to the end using the current pointer and then copy all the data in.
void InsertStudent(char givenName[50], int age, MyNode* head){
if(head == NULL){
head = (MyNode*) malloc(sizeof(MyNode));
strcpy(head->Name,givenName);
head->studentAge = age;
head->next = NULL;
}
if(head != NULL){
MyNode* current = head;
while(current->next != NULL){
current = current->next;
}
current->next = (MyNode*) malloc(sizeof(MyNode));
strcpy(current->next->Name,givenName);
current->next->studentAge = age;
current->next->next = NULL;
}
}
Now i am not sure if there is a problem in my printing or inserting because it doesn't print my nodes when i try the code out
void PrintList(MyNode* head){
MyNode* current = head;
while(current != NULL){
printf("Name is %s Age is %d\n",current->Name,current->studentAge);
current = current->next;
}
}
this is my main function.. is there a problem with the MyNode* head = NULL; line of code is that allowed?
int main()
{
MyNode* head = NULL;
int r = 0;
while(r!=1)
{
printf("Data Structures - Linked List\n");
printf("Choose one Option:\n\n");
printf("1.Insert Student\n");
printf("2.Remove Student\n");
printf("3.Print all student\n");
printf("4.Exit\n");
int option=0;
char givenName[50];
int givenAge;
scanf("%d",&option);
switch(option){
case 1:
printf("Enter name of student: ");
scanf("%s",givenName);
printf("\nEnter Age of student: ");
scanf("%d",&givenAge);
InsertStudent(givenName,givenAge,head);
break;
case 2:
printf("Enter name of student: ");
scanf("%s",givenName);
printf("\nEnter Age of student: ");
scanf("%d",&givenAge);
RemoveStudent(givenName,givenAge);
break;
case 3:
PrintList(head);
break;
case 4:
r=1;
break;
default:
r=1;
printf("\nNot an option\n");
break;
}
}
}
You're not setting the initial value of the head pointer to the first node, and since that is never done, the list remains empty and you leak memory like a sieve leaks rain water.
As you have communicated you want to use pointer-to-pointer syntax, the result should look like this . (sans error checking, which you shoudl probably consider adding):
void InsertStudent(char givenName[50], int age, MyNode** head)
{
while (*head)
head = &(*head)->next;
*head = malloc(sizeof **head);
strcpy((*head)->Name, givenName);
(*head)->studentAge = age;
(*head)->next = NULL;
}
Invoked from your main program using the address of the head pointer (do NOT confused that with the address held in the head pointer which you're initially setting to NULL correctly; think of the latter a value held by a pointer, the former as a residence where the head pointer itself is in memory).
InsertStudent(givenName,givenAge, &head); // NOTE THIS
I leave the task of removal and list cleanup.
You are passing head by value; which means that the line in InsertStudent:
head = (MyNode*) malloc(sizeof(MyNode))
which does not update the variable ‘head’ in main.
What you want is to pass &head to InsertStudent, but then InsertStudent has to deal with a MyNode **. The other option is have InsertStudent return head, so that its invocation is:
head = InsertStudent(name, age, head);
It doesn’t matter much either way, some people prefer the latter because it looks more functional.
Inside of InsertStudent, you add the first element twice. This is almost certainly unwanted. By the time you get to the line:
if(head != NULL){
head is never NULL; if it were, you would have assigned it in the if statement above. You probably want this statement to be:
else {

Need help checking a linked list in C

I'm having a issue on searching through a linked list. I'm making a grade book program and I'm doing input error checks to see if the user entered a existing course to enroll a student to said course.
So this is the struct for the course info with a doubly linked list.
typedef struct Course_Info // Course information
{
int Course_ID;
char Course_Name[15];
struct Course_Info *next;
} Course;
typedef struct // Linked list for Course
{
int Ctracker; // Keeps track of courses
Course *Course_Head;
Course *Course_Tail;
} List_Course;
And their corresponding variables along with initialization.
List_Student Students;
List_Course Courses;
Grade_List Grades;
Students.Stracker = 0;
Students.Student_Head = Students.Student_Tail = NULL;
Courses.Ctracker = 0;
Courses.Course_Head = Courses.Course_Tail = NULL;
Grades.Grade_cnt = 0;
Grades.Grade_Head = Grades.Grade_Tail = NULL;
In this function I'm going to enroll a student to a course but first im going to do some input checking to make sure the course exist.
void EnrollStudent(List_Course *Courses, List_Student *Students)
{
int CID; int SID;
printf("Enter course ID: ");
scanf("%d%*c", &CID);
if( CID != Courses -> Course_Head -> Course_ID)
{
printf("Course does not exist!\n");
return;
}
else
{
printf("Found class!\n");
}
}
The problem with what I currently have is that it only searches the first element of the linked list. How do I go about making a loop that checks the entire linked list?
Iterating a linked list is quite straightforward.
You need to use a local variable which is the current element of the list, which you init to Courses->Course_Head, eg:
Course* current = Courses->Course_Head;
then until current != NULL you just keep updating the current to point to the next element, eg:
while (current != NULL) {
// do what your want with current
current = current->next;
}
Mind that in your example you speak about a doubly linked list but it's a single linked list with two pointers to head and tail, a double linked list has two pointers for each node in both directions so that you can traverse it in reverse order, which is not the case in your situation.
ListCourse * current = Courses->Course_Head;
while ( (NULL != current) && (CID != current->Course_ID) ) current = current->next;
if (NULL == current) printf("Course %d not found\n", CID);
else printf("Course %d found\n", CID);
Your problem is that you are not iterating over the list, rather you are only checking the list head. You need to maintain a pointer to the node you are checking and iterate it (point it to the next node) in case you didn't find what you were looking for. You exit if there is nothing left to search or you found what you were looking for.

Simple stack program in C

I am in an OS class and I have to write a simple stack program (the main function just determines what the user is asking you to do). If this were not required to be in C, I would have had this done ages ago, but because I am not very good at C coding, it has "a bug"... The bug so far is that it just continues to "pop" the same value off. (It's not actually popping anything off). I think it's because I don't understand how structures and pointers really work. Or is it a not so obvious coding mistake?
#include <stdio.h>
struct node {
int data;
struct node *next;
struct node *prev;
} first;
void push(int);
void pop();
int main(void)
{
int command = 0;
while (command != 3)
{
printf("Enter your choice:\n1) Push integer\n2) Pop Integer\n3) Quit.\n");
scanf("%d",&command);
if (command == 1)
{
// push
int num;
scanf("%d",&num);
push(num);
}
else
{
if (command == 2)
{
pop();
}
else
{
if (command != 3)
{
printf("Command not understood.\n");
}
}
}
}
return 0;
}
void push (int x)
{
struct node newNode;
newNode.data = x;
newNode.prev = NULL;
newNode.next = &first;
first = newNode;
printf("%d was pushed onto the stack.\n", first.data);
}
void pop()
{
if (first.data == '\0')
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first.data);
first = *(first.next);
first.prev = NULL;
}
first should be a pointer. change it to struct node *first;
in main initialize first=NULL;
change you push/pop operations as below,
void push (int x)
{
struct node *newNode;// It should be a pointer
newNode = (struct node *)malloc(sizeof(struct node));
newNode->data = x;
//newNode.prev = NULL; // You don't need this
newNode->next = first;
first = newNode;
printf("%d was pushed onto the stack.\n", first->data);
}
void pop()
{
struct node *prevPtr;
//if (first.data == '\0')
if (first == NULL) // check if stack is empty
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first->data);
prevPtr = first;
first = first->next;
free(prevPtr);
}
The problem is that first is a single global node, and it's the only node you ever have (aside from a temporary local node inside your call to push).
This line:
first = newNode;
just copies the contents of newNode over into first; and since newNode.next is pointing to first, this means that now first.next is pointing to first, so you have a single-element circular linked list.
Similarly, this line:
first = *(first.next);
just copies the contents of *(first.next) over into first; which is a no-op, since (due to the above), *(first.next) is first.
To solve this problem, you actually need to dynamically create nodes, using malloc (and free). And your global first variable should be a pointer — a node * — that always points to the top element of the stack. (Better yet, your push and pop functions should take first as an argument, rather than having this as a global variable. There's no need for these functions to only allow a single stack to exist.)
What's the value of &first? Hint, it's always the same since first is statically allocated. Even if you change the contents of the structure, the address won't change. This might tell you why there's a bug in push. You'll need to use malloc and free if you are going to have a structure of varying size.
When you have to manage memory yourself, as C requires you to do, you need to know the difference between areas of memory known as the stack and the heap. (This "stack" is slightly different than the data structure you are creating in your program.)
Your push() function is creating a new node on the stack; when the function exits the stack is popped and the memory occupied by your new node is up for grabs. The fact that you see values you entered is due to the fact that your program is very simple. If it was calling other functions that did other things they would almost certainly overwrite that part of the stack and when you called pop(), you would see garbage.
As others have indicated, you need to use the functions malloc() and free(), which give you memory from the heap instead of the stack.
If you want to make a stack with a linked list, make first variable as a pointer. then, when you push a new node to the stack, make a new node by allocating on the heap memory by malloc(). I know that you intend to use it to point to the top of the stack. right?
In your code, first variable is overwritten by a new node since it is not a pointer variable but a value variable. That makes a result to lost a top node of the stack.
void pop()
{
struct node *prevPtr;
//if (first.data == '\0')
if (first == NULL)
{
printf("Error: Stack Empty.\n");
return;
}
printf("%d was popped off the stack.\n", first->data);
prevPtr = first;
first = first->next;
free(prevPtr);
}
#include<stdio.h>
# define max 10
int stack[max],top=-1,size=0;
void push()
{
if(top==(max-1))
{
printf("stack full\n");
}
else
{
top++;
printf("enter the value which you want to insert\n");
scanf("%d",&stack[top]);
}
}
void pop()
{
int str;
if(top==-1)
{
printf("stack empty\n");
}
else
{
str=stack[top];
top--;
printf("the removed element is %d\n",str);
}
}
void display()
{
int i;
for(i=0;i<top;i++)
{
printf("%d\n",stack[i]);
}
}
void main()
{
int enter,x;
do
{
printf("enter 1 for push the element in the array\n");
printf("enter 2 for pop the element in the array\n");
printf("enter 3 for display the element in the array\n");
scanf("%d",&enter);
switch(enter)
{
case 1:push();
break;
case 2:pop();
break;
case 3:display();
break;
default:
printf("invalid syntax");
}
printf("for continue press 0\n");
scanf("%d",&x);
}
while(x==0);
}

Resources