2

The prompt I was given asks for a program in c that implements a linked list and gives users options to perform different functions on the linked list. The needed functions are:

  • isempty(): checks if the list is empty and returns values indicating whether it is empty or not
  • add(): add an element to the tail of the list
  • insert(): insert an element at a specific index in the list
  • find(): find the index that a specific value is stored at. . If the function fails return a value indicating that
  • get(): get the value stored at a specific index
  • remove(): remove the first occurance of a specific value in the list.If the function fails return a value indicating that
  • replace(): replace the value stored at a specific index
  • delete(): delete the element at a specific index. . If the function fails return a value indicating that
  • list(): print the elements of the list in the format [a, b, c]

When I call the add_tail function it allows me to enter the value I wish to add, but after about a minute I get a message in the terminal saying the program was killed. What could be causing that? I'm also having issues with the while loop in the main function that is used to print the menu and take the users input. The menu will print normally when I first run the program, but after an action is completed the menu prints once but skips over the user input, and then prints again but accepts the user input the second time.

header file:

#ifndef LAB8_H_
#define LAB8_H_

#define FIND 'F'
#define EMPTY 'E'
#define ADD 'A'
#define INSERT 'I'
#define DELETE 'D'
#define REMOVE 'R'
#define REPLACE 'S'
#define GET 'G'
#define LIST 'L'
#define QUIT 'Q'
#define FAIL -100

struct node_t {
        struct node_t * next;
        int value;
};

struct node_t * create_node(int value);
struct node_t * add_tail(struct node_t * head, int value);
struct node_t * insert_node(struct node_t * head, int index, int value);
int is_empty(struct node_t * head);
int find_val(struct node_t * head, int value);
int get_value(struct node_t * head, int index);
struct node_t * replace_val(struct node_t * head, int index, int value);
struct node_t * remove_val(struct node_t * head, int value);
struct node_t * delete_node(struct node_t * head, int index);
void print_list(struct node_t * head);

#endif 

.c file:

#include <stdio.h>
#include <stdlib.h>
#include "func.h"

struct node_t * create_node(int value)
{
        struct node_t * new_node = malloc(sizeof(struct node_t));
        new_node -> next = NULL;
        new_node -> value = value;

        return new_node;
}


struct node_t * add_tail(struct node_t * head, int value)
{
        struct node_t * tmp;

        if(head == NULL){
                printf("Cannot add tail to an empty list. Try again\n");
                return NULL;
        } else{
                while(head != NULL){
                        if(head -> next == NULL){
                                tmp = create_node(value);
                                head -> next = tmp;
                        } else{
                                head = head -> next;
                        }
                }
        }

        return head;
}


struct node_t * insert_node(struct node_t * head, int index, int value)
{
        struct node_t * tmp;
        struct node_t * new;

        if(index < 0){
                printf("Index cannot be a negative number. Please try again\n");
                return head;
        }

        if(index == 0){
                tmp = head;
                head = create_node(value);
                head -> next = tmp;
        } else {
                tmp = head;

                while(tmp != NULL){
                        if (index == 1){
                                struct node_t * prev = tmp;
                                tmp = tmp -> next;
                                new = create_node(value);
                                new -> next = tmp;
                                prev -> next = new;
                                head = prev;
                        } else if((tmp -> next == NULL) && (index != 0)){
                                printf("The index is not found in the bounds of the list. Try again\n");
                                return head;
                        } else{
                                tmp = tmp -> next;
                                index--;
                        }
                }
        }

        return head;
}

int is_empty(struct node_t * head)
{
        if(head == NULL){
                return 0;
        } else{
                return 1;
        }
}

int find_val(struct node_t * head, int value)
{
        int index = 0;

        if(head == NULL){
                printf("Cannot find value in empty list! Try again\n");
                index = -100;
        } else{
                while(head != NULL){
                        if((head -> next == NULL) && (head -> value != value)){
                                printf("The value does not exist in the list\n");
                                index = -100;
                        } else if(head -> value == value){
                                return index;
                        } else{
                                head = head -> next;
                                index++;
                        }
                }
        }

        return index;
}

int get_value(struct node_t * head, int index)
{
        int value;

        if(index < 0){
                printf("Index cannot be a negative number. Try again\n");
                value = -100;
        } else if(head == NULL){
                printf("Cannot find index in empty list. Try again\n");
                value = -100;
        } else{
                while(head != NULL){
                        if(index == 0){
                                value = head -> value;

                        } else if((head -> next == NULL) && (index != 0)){
                                printf("Index does not exist in the bounds of the list. Try again\n");
                                value= -100;
                        } else{
                                head = head -> next;
                                index--;
                        }
                }
        }

        return value;
}

struct node_t * replace_val(struct node_t * head, int index, int value)
{
        struct node_t * tmp;

        if(index < 0){
                printf("Index cannot be a negative number. Try again\n");
                return NULL;
        } else if(head == NULL){
                printf("Cannot replace elements in an empty list. Try again\n");
                return NULL;
        } else{
                while(head != NULL){
                        if (index == 0){
                                tmp = head;
                                tmp -> value = value;
                                free(head);
                                head = tmp;
                        } else if((head -> next == NULL) && (index != 0)){
                                printf("Index does not exist is the bounds of the list. Try again\n");
                                return NULL;
                        } else{
                                head = head -> next;
                                index--;
                        }
                }
        }

        return head;
}

struct node_t * remove_val(struct node_t * head, int value)
{
        struct node_t * tmp;

        if(head == NULL){
                printf("Value cannot be found in an empty list. Try again\n");
                return NULL;
        } else{
                while(head != NULL){
                        if((head -> next == NULL) && (head -> value != value)){
                                printf("The value does not exist in the list. Try again\n");
                                return NULL;
                        } else if(head -> next -> value == value){
                                tmp = head -> next;
                                head -> next = tmp -> next;
                                free(tmp);
                        } else{
                                head = head -> next;
                        }
                }
        }

        return head;
}

struct node_t * delete_node(struct node_t * head, int index)
{
        struct node_t * tmp;

        if(index < 0){
                printf("Index cannot be a negative number. Try again\n");
                return NULL;
        } else if(head == NULL){
                printf("Cannot delete elements from an empty list. Try again\n");
                return NULL;
        } else{
                while(head != NULL){
                        if((head -> next == NULL) && (index != 0)){
                                printf("The index is not found in the bounds of the list. Try again\n");
                                return NULL;
                        } else if(index == 1){
                                tmp = head;
                                head = head -> next;
                                tmp -> next = head -> next;
                                head -> next = NULL;
                                free(head);
                                head = tmp;
                        } else{
                                head = head -> next;
                                index--;
                        }
                }
        }

        return head;
}

void print_list(struct node_t * head)
{
        if (head == NULL){
                printf("The list is empty. Nothing to print\n");
        } else{
                while(head != NULL){
                        if(head -> next != NULL){
                                printf("%d, ", head -> value);
                        } else{
                                printf("%d", head -> value);
                        }

                        head = head -> next;
                }
        }
}

main file:

#include <stdio.h>
#include <stdlib.h>
#include "func.h"

int main(void)
{
        struct node_t * head = NULL;
        int index, value;
        char choice;
        int cont = 0;

        while(cont == 0){
                printf("Welcome to the Linked List operator. Here are the functions that can be performed:\n");
                printf("A) Add element to the end of the list\n");
                printf("I) Insert and element at a specific index\n");
                printf("E) Check if the list is empty\n");
                printf("F) Find a value in the list and return the index\n");
                printf("G) Get the value at a specific index\n");
                printf("S) Replace the value at a specific index\n");
                printf("R) Remove the first occurance of a specific value\n");
                printf("D) Delete the element at a specific index\n");
                printf("L) List all the elements currently stored\n");
                printf("Q) Quit the program\n");
                printf("Please enter which action you would like to perform: ");
                scanf("%c", &choice);

                switch(choice){
                        case ADD:
                                printf("Please enter the value you would like to add: ");
                                scanf("%d", &value);

                                head = add_tail(head, value);

                                break;
                        case INSERT:
                                printf("Please enter the index at which you want to insert a new element: ");
                                scanf("%d", &index);
                                printf("Please enter the value you would like to insert: ");
                                scanf("%d", &value);

                                head = insert_node(head, index, value);

                                break;
                        case EMPTY:
                                if(is_empty(head) == 0){
                                        printf("The list is empty!\n");
                                } else if(is_empty(head) == 1){
                                        printf("The list is not empty!\n");
                                } else{
                                        printf("Something went wrong\n");
                                }

                                break;
                        case FIND:
                                printf("Please enter the value that you would like to find in the list: ");
                                scanf("%d", &value);

                                index = find_val(head, value);

                                if(index == FAIL){
                                        printf("Error. Try again\n");
                                } else{
                                        printf("The index that the value %d exists at is %d\n", value, index);
                                }

                                break;
                        case GET:
                                printf("Please enter the index for which you would like to know the value: ");
                                scanf("%d", &index);

                                if(value == FAIL){
                                        printf("Error. Try again\n");
                                } else{
                                        printf("The value of the element at index %d is %d\n", index, value);
                                }

                                break;
                        case REPLACE:
                                printf("Please enter the index of the element that you would like to replace: ");
                                scanf("%d", &index);
                                printf("Please enter the new value: ");
                                scanf("%d", &value);

                                if(replace_val(head, index, value) == NULL){
                                        printf("Error. Could not replace node\n");
                                } else{
                                        printf("Success. Here is the new list:\n");
                                        print_list(head);
                                }

                                break;
                        case REMOVE:
                                printf("Please enter the value that you would like to remove the first occurance of: ");
                                scanf("%d", &value);

                                if(remove_val(head, value) == NULL){
                                        printf("Error. Could not remove node\n");
                                } else{
                                        printf("Success! Here is the new list:\n");
                                        print_list(head);
                                }

                                break;
                        case DELETE:
                                printf("Please enter the index of the element you would like to delete: ");
                                scanf("%d", &index);

                                if(delete_node(head, index) == NULL){
                                        printf("Error. Could not delete selected element\n");
                                } else{
                                        printf("Success! Here is the new list:\n");
                                        print_list(head);
                                }

                                break;
                        case LIST:
                                printf("[");
                                print_list(head);
                                printf("]\n");

                                break;
                        case QUIT:
                                printf("You have chosen to quit the program. Goodbye!\n");
                                cont = 1;

                                break;
                        default:
                                printf("You entered an invalid choice. Please try again. Goodbye!\n");
                }
        }

        return 0;
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
kei_cse_26
  • 25
  • 5
  • Please provide a minimal program with the function that you think does not work. – Vlad from Moscow Apr 05 '23 at 18:43
  • 1
    You can ask yourself: when will `while(head != NULL){` inside `add_tail` stop? Or you could also step through it with a debugger, that should help. And `return head` may also not be a great idea. – teapot418 Apr 05 '23 at 18:52
  • Put a `printf` statement above `tmp = create_node(value);` in `add_tail` and you will see you have an infinite loop. – Fiddling Bits Apr 05 '23 at 19:00
  • _Side note:_ Stylistically, change all (e.g.) `x -> y` to `x->y`. The arrow operator (`-->`) has tight binding. No experienced programmer will put spaces around it. – Craig Estey Apr 06 '23 at 00:22

2 Answers2

2

This if statement within the function add_tail

    if(head == NULL){
            printf("Cannot add tail to an empty list. Try again\n");
            return NULL;

does not make sense. It should be removed.

Within the while loop in the function the pointer head is changed.

while(head != NULL){
        if(head -> next == NULL){
                tmp = create_node(value);
                head -> next = tmp;
        } else{
                head = head -> next;
        }
}

And after adding a node the loop is not interrupted. It will again add a new node. That is the loop is infinite.

But even if you will insert the break statement

while(head != NULL){
        if(head -> next == NULL){
                tmp = create_node(value);
                head -> next = tmp;
                break;
        } else{
                head = head -> next;
        }
}

the returned pointer from the function will not be the pointer that points to the head node of the list.

The function can be defined the following way

struct node_t * add_tail( struct node_t *head, int value )
{
    struct node_t *new_node = create_node( value );

    if ( head == NULL )
    {
        head = new_node;
    }
    else
    {
        struct node_t *current = head;
        
        while ( current->next != NULL ) current = current->next;

        current->next = new_node;
    }

    return head;
}

I am sure there are other problems with your code in your program. You can ask a new question after updating the function add_tail to which you are referring in this your question.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • @teapot418 Truly speaking I have no any desire to examine all his code.:) He should provide a minimal program that demonstrates the problem. Let he ask a new question relative another problem. It will be useful for him. – Vlad from Moscow Apr 05 '23 at 19:00
0

Note: There were a few questions by OP related to this one:

  1. Implementing a single linked list in C using recursion: What am I doing wrong?
  2. Issues with Linked List in C This question!
  3. Segmentation Fault when modifying linked list

(3) is the question I would like to have answered, but it was closed before I could do so. So, I'm answering it here. The code is similar (and I've taken the example code from (3)).


Unfortunately, there were many issues ...

  1. add_tail had if (head == NULL) which was bad (and could have segfaulted).
  2. Usually, no need to special case empty lists or lists with one element (which occurred in a number of functions).
  3. In insert_node, it wasn't clear if insertion should occur before or after the given insertion index (in the code below, "before" was chosen).
  4. The loop logic in most functions was overly complicated [and error prone].
  5. In functions that can fail (i.e. functions that don't find an index or value), returning NULL for head is an error. It leaks the entire list.
  6. The scanf for the choice was broken (e.g. it needed to be scanf(" %s",&choice); [note the extra space].
  7. Too much replicated code in main for getting user input (see the use of the get* functions in the code below).
  8. Because of the prompts for input from stdin, it makes it hard to replay a given bug (e.g. store input data in a file that is used (e.g.) ./myprogram < inp.txt).

Here is the refactored code. It is annotated with the bugs and fixes. I've recoded all functions, and tested some but not all.

Some functions that fail on index or value not found, empty list, etc. still need some cleanup.

I've added getstr and getval functions to allow for better replay testing.

#include <stdio.h>
#include <stdlib.h>
#if 1
#include <ctype.h>
#include <termios.h>
#include <string.h>
#endif

#define ADD 'A'
#define INSERT 'I'
#define DELETE 'D'
#define REMOVE 'R'
#define REPLACE 'S'
#if 1
#define PRINT 'P'
#endif

struct node_t {
    struct node_t *next;
    int value;
};

struct node_t *create_node(int value);
struct node_t *add_tail(struct node_t *head, int value);
struct node_t *insert_node(struct node_t *head, int index, int value);
#if 0
struct node_t *replace_val(struct node_t *head, int index, int value);
#else
struct node_t *replace_index(struct node_t *head, int index, int value);
#endif
struct node_t *remove_val(struct node_t *head, int value);
struct node_t *delete_node(struct node_t *head, int index);
void print_list(struct node_t *head);
#if 1
char *getstr(const char *prompt);
int getnum(const char *prompt);
#endif

#ifndef DOABORT
#define DOABORT     1
#endif

int
main(void)
{
    struct node_t *head = NULL;
    int index, value;
    char *choice;
    int cont = 0;

    while (cont == 0) {
        printf("\nWelcome to the Linked List operator.\nHere are the functions that can be performed:\n");
        printf("A) Add element to the end of the list\n");
        printf("I) Insert and element at a specific index\n");
        printf("S) Replace the value at a specific index\n");
        printf("R) Remove the first occurance of a specific value\n");
        printf("D) Delete the element at a specific index\n");
        printf("P) Print the list\n");

        choice = getstr("Please enter which action you would like to perform: ");
        if (choice == NULL)
            break;

        choice[0] = toupper((unsigned char) choice[0]);

        switch (choice[0]) {
        case ADD:
            value = getnum("Please enter the value you would like to add: ");
            head = add_tail(head, value);
            break;

        case INSERT:
            index = getnum("Please enter the index at which you want to insert a new element: ");
            value = getnum("Please enter the value you would like to insert: ");

            head = insert_node(head, index, value);
            break;

        case REPLACE:
            index = getnum("Please enter the index of the element that you would like to replace: ");
            value = getnum("Please enter the new value: ");

            if (replace_index(head, index, value) == NULL) {
                printf("Error. Could not replace node\n");
            }
            else {
                printf("Success. Here is the new list:\n");
                print_list(head);
            }
            break;

        case REMOVE:
            value = getnum("Please enter the value that you would like to remove the first occurance of: ");

            if (remove_val(head, value) == NULL) {
                printf("Error. Could not remove node\n");
            }
            else {
                printf("Success! Here is the new list:\n");
                print_list(head);
            }
            break;

        case DELETE:
            index = getnum("Please enter the index of the element you would like to delete: ");

            if (delete_node(head, index) == NULL) {
                printf("Error. Could not delete selected element\n");
            }
            else {
                printf("Success! Here is the new list:\n");
                print_list(head);
            }
            break;

        case PRINT:
            print_list(head);
            break;

        default:
            printf("You entered an invalid choice. Please try again. Goodbye!\n");
            break;
        }
    }

    return 0;
}

//create a new node
struct node_t *
create_node(int value)
{
    struct node_t *new_node = malloc(sizeof(struct node_t));

    new_node->next = NULL;
    new_node->value = value;

    return new_node;
}

//insert an element at the end of the list
struct node_t *
add_tail(struct node_t *head, int value)
{
    struct node_t *tmp;

#if 0
// NOTE/BUG: this is a defect and returning NULL is _not_ the solution
// NOTE/BUG: no need to special case this
    if (head == NULL) {
        printf("Cannot add tail to an empty list. Try again\n");
        head = NULL;
    }
// NOTE/BUG: changing head to head->next breaks the list
    else {
        while (head != NULL) {
            if (head->next == NULL) {
                tmp = create_node(value);
                head->next = tmp;
            }
            else {
                head = head->next;
            }
        }
    }
#else
    // create the new node
    tmp = create_node(value);

    // find the tail of the list
    struct node_t *tail = NULL;
    for (struct node_t *cur = head;  cur != NULL;  cur = cur->next)
        tail = cur;

    // add to tail of non-empty list
    if (tail != NULL)
        tail->next = tmp;
    // add to empty list
    else
        head = tmp;
#endif

    return head;
}

//insert a new element at index i
struct node_t *
insert_node(struct node_t *head, int index, int value)
{
    struct node_t *tmp;

    if (index < 0) {
        printf("Index cannot be a negative number. Please try again\n");
        return head;
    }

// NOTE/BUG: it's unclear whether inserting _at_ a given index is inserting
// _before_ this index or _after_ -- we have to choose so we'll choose "before"
// [below]

#if 0
// NOTE/BUG: no need to special case
    if (index == 0) {
        tmp = head;
        head = create_node(value);
        head->next = tmp;
    }
    else {
        tmp = head;

        while (tmp != NULL) {
// NOTE/BUG: no need to special case this
            if (index == 1) {
                struct node_t *prev = tmp;

                tmp = tmp->next;
                new = create_node(value);
                new->next = tmp;
                prev->next = new;
                head = prev;
            }
            else if ((tmp->next == NULL) && (index != 0)) {
                printf("The index is not found in the bounds of the list. Try again\n");
                return head;
            }
            else {
                tmp = tmp->next;
                index--;
            }
        }
    }
#else
// NOTE/FIX: we insert _before_ index (so we can insert before the head with 0)
    // create new node
    tmp = create_node(value);

    // find the Nth index and insert before it
    struct node_t *cur = head;
    struct node_t *prev = NULL;
    int curidx = 0;
    for (;  cur != NULL;  cur = cur->next, ++curidx) {
        if (curidx == index) {
            if (prev != NULL)
                prev->next = tmp;
            else
                head = tmp;
            tmp->next = cur;
            break;
        }

        prev = cur;
    }

    do {
        // we did the insert in above loop
        if (cur != NULL)
            break;

        // no index found
        // we can add to tail (or abort)
        if (prev != NULL)
            prev->next = tmp;
        else
            head = tmp;
    } while (0);
#endif

    return head;
}

//replace the value of the element at index i
struct node_t *
replace_index(struct node_t *head, int index, int value)
{

    if (index < 0) {
        printf("Index cannot be a negative number. Try again\n");
        exit(1);
    }
    else if (head == NULL) {
        printf("Cannot replace elements in an empty list. Try again\n");
        exit(1);
    }

#if 0
// NOTE/BUG: changing head to head->next breaks the list
// NOTE/BUG: no need to free head -- this leaks memory and is undefined behavior
// NOTE/BUG: doing tmp = head before this does _not_ fix this
    else {
        while (head != NULL) {
            if (index == 0) {
                tmp = head;
                tmp->value = value;
                free(head);
                head = tmp;
            }
            else if ((head->next == NULL) && (index != 0)) {
                printf("Index does not exist is the bounds of the list. Try again\n");
                exit(1);
            }
            else {
                head = head->next;
                index--;
            }
        }
    }
#else
    struct node_t *cur = head;
    int curidx = 0;
    for (;  cur != NULL;  cur = cur->next, ++curidx) {
        if (curidx == index) {
            cur->value = value;
            break;
        }
    }

    // we could _not_ find the correct index -- abort or just ignore?
    // NOTE: with a boolean return, we could just return true if the index
    // was found
#if DOABORT
    if (cur == NULL) {
        printf("replace_index: index=%d not found\n",index);
        exit(1);
    }
#endif
#endif

    return head;
}

//remove the first occurance of a value x in the list
struct node_t *
remove_val(struct node_t *head, int value)
{

#if 0
// NOTE/BUG: removing by value in an empty list _is_ valid
    if (head == NULL) {
        printf("Value cannot be found in an empty list. Try again\n");
        return NULL;
    }
// NOTE/BUG: changing head to head->next breaks the list
    else {
        while (head != NULL) {
            if ((head->next == NULL) && (head->value != value)) {
                printf("The value does not exist in the list. Try again\n");
                return NULL;
            }
            else if (head->next->value == value) {
                tmp = head->next;
                head->next = tmp->next;
                free(tmp);
            }
            else {
                head = head->next;
            }
        }
    }
#else
    struct node_t *next;
    struct node_t *prev = NULL;
    struct node_t *cur = head;
    for (;  cur != NULL;  cur = next) {
        next = cur->next;

        if (cur->value == value) {
            if (prev != NULL)
                prev->next = next;
            else
                head = next;
            free(cur);
            break;
        }

        prev = cur;
    }

    // same issue as above -- do we abort of just ignore
#if DOABORT
    if (cur == NULL) {
        printf("remove_val: value=%d not found\n",value);
        exit(1);
    }
#endif
#endif

    return head;
}

//delete the node at index i
struct node_t *
delete_node(struct node_t *head, int index)
{

    if (index < 0) {
        printf("Index cannot be a negative number. Try again\n");
        return NULL;
    }

#if 0
// NOTE/BUG: no need to special case an empty list
    else if (head == NULL) {
        printf("Cannot delete elements from an empty list. Try again\n");
        return NULL;
    }
    else {
        while (head != NULL) {
            if ((head->next == NULL) && (index != 0)) {
                printf("The index is not found in the bounds of the list. Try again\n");
                return NULL;
            }
            else if (index == 1) {
                tmp = head;
                head = head->next;
                tmp->next = head->next;
                head->next = NULL;
                free(head);
                head = tmp;
            }
            else {
                head = head->next;
                index--;
            }
        }
    }
#else
    struct node_t *cur = head;
    struct node_t *next;
    struct node_t *prev = NULL;
    int curidx = 0;
    for (;  cur != NULL;  cur = next, ++curidx) {
        next = cur->next;

        if (curidx == index) {
            if (prev != NULL)
                prev->next = next;
            else
                head = next;
            break;
        }

        prev = cur;
    }

#if DOABORT
    if (cur == NULL) {
        printf("delete_node: index=%d not found\n",index);
        exit(1);
    }
#endif
#endif

    return head;
}

//print elements of the list
void
print_list(struct node_t *head)
{
    if (head == NULL) {
        printf("The list is empty. Nothing to print\n");
    }
#if 0
    else {
        while (head != NULL) {
            if (head->next == NULL) {
                printf("%d", head->value);
            }
            else {
                printf("%d, ", head->value);
                head = head->next;
            }
        }
    }
#else
    for (struct node_t *cur = head;  cur != NULL;  cur = cur->next) {
        if (cur != head)
            printf(", ");
        printf("%d",cur->value);
    }
    printf("\n");
#endif
}

#if 1
char *
getstr(const char *prompt)
{
    static char buf[100];
    char *bp;

    // echo input if _not_ a TTY
    static int echoflg = -1;
    if (echoflg < 0) {
        struct termios tio;
        echoflg = (tcgetattr(fileno(stdin),&tio) < 0);
    }

    while (1) {
        printf("%s",prompt);
        fflush(stdout);

        bp = fgets(buf,sizeof(buf),stdin);

        if (bp == NULL) {
            if (echoflg)
                printf("EOF\n");
            break;
        }

        if (echoflg)
            fputs(buf,stdout);

        buf[strcspn(buf,"\n")] = 0;

        // stop if _not_ blank line
        if (buf[0] != 0)
            break;
    }

    return bp;
}

int
getnum(const char *prompt)
{
    char *buf = getstr(prompt);
    int val = -1;

    if (buf != NULL)
        val = strtol(buf,&buf,10);

    return val;
}
#endif

In the code above, I've used cpp conditionals to denote old vs. new code:

#if 0
// old code
#else
// new code
#endif

#if 1
// new code
#endif

Note: this can be cleaned up by running the file through unifdef -k


Here is the sample input file I used:

a
1

a
2

a
3

a
4

a
5

a
6

a
7

p

r
5

d
5

s
4
9

Here is the sample output for that input file:


Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform: a
Please enter the value you would like to add: 1

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 2

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 3

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 4

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 5

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 6

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: a
Please enter the value you would like to add: 7

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: p
1, 2, 3, 4, 5, 6, 7

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: r
Please enter the value that you would like to remove the first occurance of: 5
Success! Here is the new list:
1, 2, 3, 4, 6, 7

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: d
Please enter the index of the element you would like to delete: 5
Success! Here is the new list:
1, 2, 3, 4, 6

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform:
Please enter which action you would like to perform: s
Please enter the index of the element that you would like to replace: 4
Please enter the new value: 9
Success. Here is the new list:
1, 2, 3, 4, 9

Welcome to the Linked List operator.
Here are the functions that can be performed:
A) Add element to the end of the list
I) Insert and element at a specific index
S) Replace the value at a specific index
R) Remove the first occurance of a specific value
D) Delete the element at a specific index
P) Print the list
Please enter which action you would like to perform: EOF

UPDATE:

One of above issues is that functions that search for nodes can fail. That is, no match was found. How to communicate that back to the caller?

For linked lists, there are basically three ways to handle returning an updated head to caller:

// current way ...
node_t *
add_tail(node_t *head,int value)
{

    // do stuff and update head ...

    return head;
}

// double pointer to head ...
void
add_tail(node_t **head,int value)
{

    // do stuff and update *head ...
}

// use of list struct ...
void
add_tail(list_t *list,int value)
{

    // do stuff and update list->head ...
}

I find the third options (using a separate list struct) to be much safer and more flexible.

Because functions no longer need to return an updated head value via return head;, we can redefine the return value to be a boolean "success" value (i.e 0=fail, 1=success).

Another benefit is that the caller of a linked list function does not need to know if the function will update head or not. It just passes in the list pointer and the given function can do as it wishes.

Here is a version recoded to use a "real" list (It has also been run through unifdef -k):

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <termios.h>
#include <string.h>

#define ADD 'A'
#define INSERT 'I'
#define DELETE 'D'
#define REMOVE 'R'
#define REPLACE 'S'
#define PRINT 'P'

typedef struct node node_t;
struct node {
    node_t *next;
    int value;
};

typedef struct {
    node_t *head;
    int count;
} list_t;

node_t *create_node(int value);
void add_tail(list_t *list, int value);
int insert_node(list_t *list, int index, int value);
int replace_index(list_t *list, int index, int value);
int remove_val(list_t *list, int value);
int delete_node(list_t *list, int index);
void print_list(list_t *list);
char *getstr(const char *prompt);
int getnum(const char *prompt);

#ifndef DOABORT
#define DOABORT     1
#endif

int
main(void)
{
    list_t *list = calloc(1,sizeof(*list));
    int index, value;
    char *choice;
    int cont = 0;

    while (cont == 0) {
        printf("\nWelcome to the Linked List operator.\nHere are the functions that can be performed:\n");
        printf("A) Add element to the end of the list\n");
        printf("I) Insert and element at a specific index\n");
        printf("S) Replace the value at a specific index\n");
        printf("R) Remove the first occurance of a specific value\n");
        printf("D) Delete the element at a specific index\n");
        printf("P) Print the list\n");

        choice = getstr("Please enter which action you would like to perform: ");
        if (choice == NULL)
            break;

        choice[0] = toupper((unsigned char) choice[0]);

        switch (choice[0]) {
        case ADD:
            value = getnum("Please enter the value you would like to add: ");
            add_tail(list, value);
            break;

        case INSERT:
            index = getnum("Please enter the index at which you want to insert a new element: ");
            value = getnum("Please enter the value you would like to insert: ");

            insert_node(list, index, value);
            break;

        case REPLACE:
            index = getnum("Please enter the index of the element that you would like to replace: ");
            value = getnum("Please enter the new value: ");

            if (replace_index(list, index, value) == 0) {
                printf("Error. Could not replace node\n");
            }
            else {
                printf("Success. Here is the new list:\n");
                print_list(list);
            }
            break;

        case REMOVE:
            value = getnum("Please enter the value that you would like to remove the first occurance of: ");

            if (remove_val(list, value) == 0) {
                printf("Error. Could not remove node\n");
            }
            else {
                printf("Success! Here is the new list:\n");
                print_list(list);
            }
            break;

        case DELETE:
            index = getnum("Please enter the index of the element you would like to delete: ");

            if (delete_node(list, index) == 0) {
                printf("Error. Could not delete selected element\n");
            }
            else {
                printf("Success! Here is the new list:\n");
                print_list(list);
            }
            break;

        case PRINT:
            print_list(list);
            break;

        default:
            printf("You entered an invalid choice. Please try again. Goodbye!\n");
            break;
        }
    }

    return 0;
}

//create a new node
node_t *
create_node(int value)
{
    node_t *new_node = malloc(sizeof(node_t));

    new_node->next = NULL;
    new_node->value = value;

    return new_node;
}

//insert an element at the end of the list
void
add_tail(list_t *list, int value)
{
    node_t *tmp;

    // create the new node
    tmp = create_node(value);

    // find the tail of the list
    node_t *tail = NULL;
    for (node_t *cur = list->head;  cur != NULL;  cur = cur->next)
        tail = cur;

    // add to tail of non-empty list
    if (tail != NULL)
        tail->next = tmp;
    // add to empty list
    else
        list->head = tmp;
}

//insert a new element at index i
int
insert_node(list_t *list, int index, int value)
{
    node_t *tmp;

    if (index < 0) {
        printf("Index cannot be a negative number. Please try again\n");
        return 0;
    }

    // create new node
    tmp = create_node(value);

    // find the Nth index and insert before it
    node_t *cur = list->head;
    node_t *prev = NULL;
    int curidx = 0;
    for (;  cur != NULL;  cur = cur->next, ++curidx) {
        if (curidx == index) {
            if (prev != NULL)
                prev->next = tmp;
            else
                list->head = tmp;
            tmp->next = cur;
            break;
        }

        prev = cur;
    }

    do {
        // we did the insert in above loop
        if (cur != NULL)
            break;

        // no index found
        // we can add to tail (or abort)
        if (prev != NULL)
            prev->next = tmp;
        else
            list->head = tmp;
    } while (0);

    return 1;
}

//replace the value of the element at index i
int
replace_index(list_t *list, int index, int value)
{

    if (index < 0) {
        printf("Index cannot be a negative number. Try again\n");
        return 0;
    }
    else if (list->head == NULL) {
        printf("Cannot replace elements in an empty list. Try again\n");
        return 0;
    }

    node_t *cur = list->head;
    int curidx = 0;
    for (;  cur != NULL;  cur = cur->next, ++curidx) {
        if (curidx == index) {
            cur->value = value;
            break;
        }
    }

    if (cur == NULL) {
        printf("replace_index: index=%d not found\n",index);
        return 0;
    }

    return 1;
}

//remove the first occurance of a value x in the list
int
remove_val(list_t *list, int value)
{

    node_t *next;
    node_t *prev = NULL;
    node_t *cur = list->head;
    for (;  cur != NULL;  cur = next) {
        next = cur->next;

        if (cur->value == value) {
            if (prev != NULL)
                prev->next = next;
            else
                list->head = next;
            free(cur);
            break;
        }

        prev = cur;
    }

    // same issue as above -- do we abort of just ignore
    if (cur == NULL) {
        printf("remove_val: value=%d not found\n",value);
        return 0;
    }

    return 1;
}

//delete the node at index i
int
delete_node(list_t *list, int index)
{

    if (index < 0) {
        printf("Index cannot be a negative number. Try again\n");
        return 0;
    }

    node_t *cur = list->head;
    node_t *next;
    node_t *prev = NULL;
    int curidx = 0;
    for (;  cur != NULL;  cur = next, ++curidx) {
        next = cur->next;

        if (curidx == index) {
            if (prev != NULL)
                prev->next = next;
            else
                list->head = next;
            break;
        }

        prev = cur;
    }

    if (cur == NULL) {
        printf("delete_node: index=%d not found\n",index);
        return 0;
    }

    return 1;
}

//print elements of the list
void
print_list(list_t *list)
{
    node_t *cur = list->head;

    if (cur == NULL)
        printf("The list is empty. Nothing to print");

    for (;  cur != NULL;  cur = cur->next) {
        if (cur != list->head)
            printf(", ");
        printf("%d",cur->value);
    }

    printf("\n");
}

char *
getstr(const char *prompt)
{
    static char buf[100];
    char *bp;

    // echo input if _not_ a TTY
    static int echoflg = -1;
    if (echoflg < 0) {
        struct termios tio;
        echoflg = (tcgetattr(fileno(stdin),&tio) < 0);
    }

    while (1) {
        printf("%s",prompt);
        fflush(stdout);

        bp = fgets(buf,sizeof(buf),stdin);

        if (bp == NULL) {
            if (echoflg)
                printf("EOF\n");
            break;
        }

        if (echoflg)
            fputs(buf,stdout);

        buf[strcspn(buf,"\n")] = 0;

        // stop if _not_ blank line
        if (buf[0] != 0)
            break;
    }

    return bp;
}

int
getnum(const char *prompt)
{
    char *buf = getstr(prompt);
    int val = -1;

    if (buf != NULL)
        val = strtol(buf,&buf,10);

    return val;
}
Craig Estey
  • 30,627
  • 4
  • 24
  • 48