So I have a function that adds a node to a linked list but instead of just adding the element at the end of the linked list I am trying to get user input for the spot the user wants to add it in. I then need to add it and shift everything else over without deleting anything else. I am having a lot of trouble with this.
Below I will show my code, its a bit messy but I will explain as best I can.
void InsertGraphicElement(struct RasterGraphic *pA) {
int counter = 1;
int response = 0;
char tempString[256];
struct GraphicElement *newNode = malloc(sizeof(*newNode));
if (newNode == NULL)
return;
newNode->fileName = malloc(256 * sizeof(char));
if (newNode->fileName == NULL)
return;
newNode->pNext = NULL;
printf("Insert a GraphicElement in the RasterGraphic\nPlease enter the GraphicElement filename: ");
scanf("%s", newNode->fileName);
if (pA->GraphicElements == NULL) {
pA->GraphicElements = newNode;
printf("This is the first GraphicElement in the list\n");
} else {
struct GraphicElement *tempHead = pA->GraphicElements;
struct GraphicElement *tempTail = pA->GraphicElements;
while (tempHead->pNext != NULL) {
tempHead = tempHead->pNext;
counter++;
}
tempHead->pNext = newNode;
printf("There are %d GraphicElement(s) in the list. Please specify the position (<= %d) to insert at :", counter, counter);
scanf("%d", &response);
if (response == counter) {
return;
} else {
while (response < counter) {
tempTail = tempTail->pNext;
response++;
}
}
}
return;
}
It is incomplete Ive been messing with the code trying to figure it out but as you can see I am able to add without a problem at the end of the list. What I am having trouble with is if there are 5 elements for example in the list 1,2,3,4,,5 and I add a sixth the list would obviously look like this 1,2,3,4,5,6. What I want to do is take user input such as they want to add the sixth element to maybe spot 3 so the list would look like this 1,2,6,3,4,5. Ive tried a bunch of things a point in the right direction or some help would be greatly appreciated. Thank you,
Below are my struct definitions
struct GraphicElement {
char *fileName;
struct GraphicElement *pNext;
};
struct RasterGraphic {
//int numNodes;
struct GraphicElement *GraphicElements;
};
You currently have the following:
if (response == counter) {
return;
}
else {
while(response < counter){
tempTail = tempTail->pNext;
response++;
}
}
I would revise this to be something like:
if (response > counter+1 || response < 1) { // These are all invalid values
return;
}
else if (response == 1) { // We are replacing the head node
newNode->pNext = tempTail->pNext;
pA->GraphicElements = newNode;
}
else {
while(response-- > 2) { // Responses are 1-indexed
tempTail = tempTail->pNext;
}
newNode->pNext = tempTail->pNext; // IMPORTANT: Update newNode reference FIRST
tempTail->pNext = newNode; // Update tempTail to point to newNode
}
Disclaimer - Not tested in any way
I tried to comment things I thought were important here and there. The key thing to remember here though is that in order to insert something into a singly-linked list, you must update your new node to point to the rest of the linked list BEFORE you update the reference of the node previous to it, or else the last bit of your list will be lost forever and you will have a circular reference to the last node.
As a side note, it looks like your are traversing your entire linked-list twice each time you want to add a new element to it. I might recommend keeping a rolling counter of how many items are in the linked list that you update each time you add something to it to avoid the overhead of recalculating this value if efficiency is something you value.
Hope this helps!
EDIT: You'd have to remove the line that adds the new node to the end of the list higher up in the function for this to work, however. "tempHead->pNext = newNode;"
Related
I'm having trouble with deleting the first node in my linked list, when i print the results after deleting other nodes its a success, yet deleting the first node, its prints a 0 and the last two members of the struct.
The function is supposed to be passed a pointer to a linked-list, prompt the user for an ID number to find a delete a node, and return the list.
struct dog *delete_from_list(struct dog *dogs){
int num;
printf("Enter a dogs ID number to be deleted ");
scanf("%d", &num);
struct dog *prev, *cur;
for(cur = dogs, prev = NULL;
cur !=NULL && cur->number != num;
prev = cur, cur = cur->next);
if (cur == NULL){
printf("Dog not found");
return dogs;
}
if( prev == NULL){
dogs = dogs->next;
printf("Dog deleted");
}
else{
prev->next = cur->next;
}
free(cur);
return dogs;
}
This is the function to print the linked list afterwards
void print(struct dog *list){
/* Prints all structs within the
* linked list
*/
printf("\nID Number\t Dog Name\t Breed\t\t Owner\n");
for( ; list != NULL; list = list->next){
printf("%d\t\t %-10s\t %-10s\t %-12s\n", list->number, list->dog_name, list->breed, list->owner_last_name);
}
}
Your function apparently works fine works fine (modified it to accept num as a parameter for me...), concerning its actual intention.
What you do not get is an output "dog deleted" if you do not delete the head - this is because you did not implement to do so. Try this instead:
if (!cur)
{
puts("Dog not found");
return dogs;
}
if(!prev)
{
dogs = dogs->next;
puts("head deleted"); // <- changed "dog" -> "head"
}
else
{
prev->next = cur->next;
puts("dog deleted"); // <- added by me!
}
Important is: You absolutely need to call it (as BLUEPIXIY denoted in his comment) like this:
dogs = delete_from_list(dogs);
If you do not, your outer variable 'dogs' won't change and will point to memory already deleted. If you still use the then dangling pointer, you get undefined behaviour, most likely an access violation (segmentation fault).
To avoid such a problem, you might like to pass a pointer to pointer to your function. The return value gets free then, and you could use it to indicate if the dog actually could have been removed:
bool // need to #include <stdbool.h> for
delete_from_list(struct dog** dogs)
// ^
You'd now use *dogs instead of dogs internally and would call it like this:
delete_from_list(&dogs);
Advantage: user does not have to care about correct re-assignment...
Trying to understand how to properly setup a function that will delete from a linked list based on a user specified parameter. So I have a global variable entitled list that holds all the dog structs within them. I can get the list to delete the node the user wants removed if there are multiple structs in the linked list, for some reason though if there is only one node or the node I want deleted is the head of the list the function does not remove it. Any direction for this issue would be greatly appreciated.
void remove_one(char* name)
{
struct dog *tempList = list;
struct dog *previous = NULL;
if (tempList == NULL) {
return;
}
while (tempList != NULL) {
if (strcmpi(tempList->name, name) == 0) {
if (previous == NULL) {
tempList = tempList->next;
}
else {
previous->next = tempList->next;
free(tempList);
tempList = previous->next;
}
return;
}
else {
previous = tempList;
tempList = tempList->next;
}
}
return;
}
2 problems here:
if (previous == NULL) {
tempList = tempList->next;
}
First, tempList is a local variable, so you don't really delete the head. Next time you enter in the routine, list is still pointing to the same (valid) value.
Second, you don't free the memory.
correct code:
if (previous == NULL) {
list = tempList->next; // change global variable so head is "tempList->next"
free(tempList); // free templist memory else you get memory leak
}
I currently have a linked list and need to add data to it that is inputted by the user from the keyboard so i have two structs:
struct CourseInfo {
int courseID;
char courseName[30];
};
typedef struct CourseInfo courseinfo;
struct StudentInfo {
char StudentID[10];
char FirstName[21];
char LastName[26];
int num_course;
courseinfo array[10];
struct StudentInfo *next;
};
So i have a linked list with 3 nodes currently. I then need to call a function and add a node. The node needs to be inserted in the correct place which is that the studentID before it needs to be less than it and the studentID after needs to be greater so the current IDs i have are 111111111, 333333333, and 444444444 and im trying to add 222222222 so it would go in the second spot so my function looks like:
studentinfo *addStudent(studentinfo *data) //returns type studentinfo* now
{
studentinfo *add;
add = malloc(sizeof(studentinfo));
add->next = NULL; //Now its set to NULL to begin
int knt;
printf("%s", "Adding new student:\nStudent ID: ");
scanf("%s", add->StudentID);
printf("%s", "First Name: ");
scanf("%s", add->FirstName);
printf("%s", "Last Name: ");
scanf("%s", add->LastName);
printf("%s", "Number of courses: ");
scanf("%d", &add->num_course);
for(knt = 0; knt < add->num_course; knt++) {
printf("%s", "Course ID: ");
scanf("%d", &add->array[knt].courseID);
printf("%s", "Course Name: ");
scanf("%s", add->array[knt].courseName);
}
if(searchStudentID(data, add->StudentID)) {
puts("immediately inside if");
while(data != NULL) {
puts("Immediately inside while");
if(strcmp(add->StudentID, data->StudentID) < 0) {
puts("inside if");
add->next = data;
data = add;
}
else {
puts("inside first else");
studentinfo *PrevPtr = data;
studentinfo *NPtr = data->next;
while(NPtr != NULL) {
("inside while(NPTR != NULL)");
if(strcmp(add->StudentID, NPtr->StudentID) < 0) {
add->next = PrevPtr;
PrevPtr->next = add;
break;
}
else {
puts("inside a differnet else");
PrevPtr = NPtr;
NPtr = NPtr->next;
}
}
if(PrevPtr->next == NULL) {
puts("inside last if");
add->next = NULL;
PrevPtr->next = add;
}
}
}
}
else {
puts("Found id");
}
return data; //returns data back to call
}
So i added all those puts statement because i wanted to see why the program kept crashing. So the puts statement puts("Inside a different else") is stuck in an infinite loop and keeps printing. The function searchStudentID simply returns 1 if we dont already have the ID and 0 if we already have it. I know that this function works so there is no need to post it.
I think the problem may be in the break; statement because it doesnt exit from the first while loop but only exits from the inner loop but im not positive.The call to this function look like:
list = addStudent(list); //Now the new data is stored in list
Where list is the linked list with 3 nodes
Linked list management is about managing node pointers, not just nodes. You want to do several things to make this considerably easier on yourself:
Separate the input step from the search+insertion step. They don't belong together regardless of how they may seem otherwise. The biggest benefit this brings to you is reducing your list insertion code to what it should be doing (and only what it should be doing): managing the linked list. I've kept yours intact, but you should really be error checking and doing the data reading somewhere else.
Use a pointer to pointer to walk the list. The biggest benefit from this is eliminating the need to special-case head-position insertion. If that is the position a new node will eventually occupy, so be it, but eliminating that special-case further reduces the complexity of the algorithm.
Don't search the list unless you're capable of retaining the search results to be used for insertion logic. It makes little sense to perform an O(N) scan of the linked list to determine if input data is already present, only to search it again to find the position said-data will actually be inserted. Do it once. Find the position where it belongs. If it is already there, do nothing, otherwise, you sit at the precipice of the proper insertion location already.
Finally, don't allocate a new node unless you know you need one. Use an automatic variable that helpfully self-discards if you end up doing nothing.
Putting all of that together gives something like this:
struct CourseInfo {
int courseID;
char courseName[30];
};
typedef struct CourseInfo CourseInfo;
struct StudentInfo {
char StudentID[10];
char FirstName[21];
char LastName[26];
int num_course;
CourseInfo array[10];
struct StudentInfo *next;
};
typedef struct StudentInfo StudentInfo;
StudentInfo *addStudent(StudentInfo *head)
{
StudentInfo **pp = &head, *p = NULL, rec;
int knt;
// TODO: error check your inputs!
printf("%s", "Adding new student:\nStudent ID: ");
scanf("%s", rec.StudentID);
printf("%s", "First Name: ");
scanf("%s", rec.FirstName);
printf("%s", "Last Name: ");
scanf("%s", rec.LastName);
printf("%s", "Number of courses: ");
scanf("%d", &rec.num_course);
for(knt = 0; knt < rec.num_course; knt++) {
printf("%s", "Course ID: ");
scanf("%d", &rec.array[knt].courseID);
printf("%s", "Course Name: ");
scanf("%s", rec.array[knt].courseName);
}
// walk the list pointers, starting with head, looking for
// a node that is equal or greater than the input node
while (*pp && (knt = strcmp((*pp)->StudentID, rec.StudentID)) < 0)
pp = &(*pp)->next;
// leave now if already present
if (*pp && knt == 0)
return head;
// allocate new node
p = malloc(sizeof *p);
if (p == NULL)
{
perror("Failed to allocate new node");
exit(EXIT_FAILURE);
}
// structure copy.
*p = rec;
// link into proper list position.
p->next = *pp;
*pp = p;
// always return the head (which may have updated above)
return head;
}
That's it. As mentioned, I would personally perform the input operation somewhere other than this function, but I leave that to you to consider.
Best of luck.
Since this function updates the list you either need it to be
studentinfo *addStudent(studentifo *data)
and return the updated head value. Or
void addStudent(studentifo **data)
and do
*data = <new thing>
Issues that I see:
You are not setting add->next to NULL.
You are changing data locally.
add->next = data;
data = add;
changes the value of data locally in the function. It does not change the value in the calling function.
You have the check
while(data != NULL)
following the if statement
if(searchStudentID(data, add->StudentID)) {
but I don't see any code to add the new student when searchStudentID(data, add->StudentID) returns false and when data == NULL to start with.
This code is for adding an employee profile at the beginning of a linked list. For some reason, which i cannot identify, this code worked once, and then it just printed the printf and then exited the loop. Even after entering numerous records, it was still doing the same thing. So can anyone identify the problem??
void insert_tobeg() {
char name[15];
struct employee *newPtr;
printf("\n\n\tEnter the record to be entered:");
gets(name);
if(strcmp(start->name, name) == 0) {
curr = (struct employee*)malloc(sizeof(struct employee));
employee_entry();
curr->newPtr = start;
start = curr;
printf("\n\n\tRecord has been added at the beggining!");
return;
}
}
You manage it incorrect, you overwrite the name in first element, you need to write to the currently allocated ellement.
Try something like this
curr = (struct employee*)malloc( sizeof(struct employee));
employee_entry();
if( strcmp( curr->name, name) == 0)
{
curr->newPtr=start;
start = curr;
printf("\n\n\tRecord has been added at the beggining!");
return;
}
else
{
free(curr);
}
And don't cast malloc result in C
Did you mean while loop instead if, and remove return statement.
I've been working on this code for my shell that I'm creating and for some reason it isn't working. I'm implementing a watchuser function that watch's a user when an argument is given (args[1]). However, when a second argument (args[2]) of "off" is given, the user should be deleted from the linked list and should no longer be watched.
struct userList * goList;
goList = userInventory;
do{
if (strcmp(userInventory->username, args[1]) == 0){
printf("%s\n", args[1]);
printf("%s\n",userInventory->username);
struct userList * temp2;
temp2 = userInventory->next;
if (userInventory->next != NULL){
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
}
goList = goList->next;
}while (goList != userInventory);
My global struct is also as follows:
struct userList{
char * username;
struct userList * prev;
struct userList * next;
}
For reason, this code won't delete the user node from my linked list. The adding works, but this remove function won't and I'm not sure why. The print statements are there just to make sure it's executing the condition, which it is.
If anyone could help me find the reasoning behind my error, I'd greatly appreciate it. Till then, I'll be trying to debug this.
Thanks.
If I understand anything about your code,
Problem 1 (plausible):
goList = userInventory;
do {
...
goList = goList->next;
} while (goList != userInventory);
Is this a circular list? If it's not, the condition in while () isn't going to become true.
Problem 2:
goList = userInventory;
do {
if (strcmp(userInventory->username, args[1]) == 0) {
...
}
goList = goList->next;
} while (goList != userInventory);
Here you keep comparing the string in the head (or tail) of the list instead of comparing the string in the current node, goList. Finding a match can only succeed in the above code, if the match is in the very first node (head/tail) to which userInventory points initially.
Problem 3:
temp2 = userInventory->next;
if (userInventory->next != NULL) {
userInventory->next = temp2->next;
userInventory->next->prev = userInventory;
}
free(temp2);
Let's assume userInventory is already corrected to be goList:
temp2 = goList->next;
if (goList->next != NULL) {
goList->next = temp2->next;
goList->next->prev = goList;
}
free(temp2);
First of all, it's going to free() not the matching node, but the one after it (or maybe even NULL), which is wrong.
Secondly, this piece of code isn't doing proper unlinking and relinking of nodes. What it should be (assuming the list isn't circular):
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
Problem 4:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
free(temp2);
}
goList = goList->next;
} while (...);
If the deletion succeeds, this line is going to access the just freed node and likely crash your program:
goList = goList->next;
So, you need to change the code to something like:
do {
if (strcmp(goList->username, args[1]) == 0) {
temp2 = goList;
if (goList->next != NULL) {
goList->next->prev = goList->prev; // now goList->next node points to goList->prev node
}
if (goList->prev != NULL) {
goList->prev->next = goList->next; // now goList->prev node points to goList->next node
}
goList = goList->next;
free(temp2);
}
else
{
goList = goList->next;
}
} while (...);
Problem 5:
goList = userInventory;
If you delete the list head (or is it tail?) node, you need to update userInventory to point to the next node after it. If you don't, you will lose all access to the list because userInventory will point to freed memory and not to the remaining nodes, if any.
Problem 6 (plausible):
free(temp2);
The above line does not free() the memory behind temp2->username. You want to free() it if it was malloc()ed.
You should really approach problems one step at a time (e.g. first, iterating over a list, then unlinking/relinking nodes, then deleting nodes).
When things aren't clear or aren't working, use paper and a pencil (or a drawing board and a pen or a piece of chalk) to visualize the problem for yourself. Draw the objects, the arrows depicting pointers or some other connections between them, etc etc, scribble variable names next to the objects so you can clearly see how to progress from the diagram to code.