0

i am getting Segmentation fault: 11

after i run the menu/ command I and i want to add the inputs to the list. Trying to figure out but i couldn't any help is appreciated

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#define MAX_LENGTH 1023

//**********************************************************************
//  Linked List Definitions
//  Define your linked list node and pointer types
//  here for use throughout the file.
//
//   ADD STATEMENT(S) HERE
typedef struct contact
{
    char familyName[MAX_LENGTH+1];
    char firstName[MAX_LENGTH+1];
    char address[MAX_LENGTH+1];
    char phoneNumber[MAX_LENGTH+1];
    struct contact *link;
//pointer to next node.
} contact;
//**********************************************************************
// Linked List Function Declarations
//
// Functions that modify the linked list.
//   Declare your linked list functions here.
//
//   ADD STATEMENT(S) HERE
//**********************************************************************
// Support Function Declarations
//
//Teacher's function:

void safegets (char s[], int arraySize);        // gets without buffer overflow
void familyNameDuplicate (char familyName[]);   // marker/tester friendly
void familyNameFound (char familyName[]);       //   functions to print
void familyNameNotFound (char familyName[]);    //     messages to user
void familyNameDeleted (char familyName[]);
void phoneNumberFound (char phoneNumber[]);
void phoneNumberNotFound (char phoneNumber[]);
void printPhoneBookEmpty (void);
void printPhoneBookTitle (void);

//My funcs:

void newContact (char familyName[], char firstName[], char address[], char phoneNumber[], contact **p);//want to modify something
void contactDel (contact **p, char familyName[]);
/* remove head */
contact *contact_search (contact *c, char familyName[]);
/* By Family Name*/
contact *phone_search (contact *c, char phoneNumber[]);
void print_all (contact *head);
void DelAll (contact **p);

//**********************************************************************
// Program-wide Constants
//

const char NULL_CHAR = '\0';
const char NEWLINE = '\n';

//**********************************************************************
// Main Program
//
int main (void)
{
    contact *head=NULL;
    contact *temp=NULL;
    const char bannerString[]
        = "Personal Phone Book Maintenance Program.\n\n";
    const char commandList[]
        = "Commands are I (insert), D (delete), S (search by name),\n"
          "  R (reverse search by phone #), P (print), Q (quit).\n";

    // Declare linked list head.
    //   ADD STATEMENT(S) HERE TO DECLARE LINKED LIST HEAD.

    // announce start of program
    printf("%s",bannerString);
    printf("%s",commandList);

    char response;
    char res[MAX_LENGTH+1];
    char input[MAX_LENGTH+1];
    char familyName[MAX_LENGTH+1];
    char firstName[MAX_LENGTH+1];
    char address[MAX_LENGTH+1];
    char phoneNumber[MAX_LENGTH+1];
        //to pass char arrays to insert function
    do
    {
        printf("\nCommand?: ");
        safegets(input,MAX_LENGTH+1);
        // Response is first char entered by user.
        // Convert to uppercase to simplify later comparisons.
        response = toupper(input[0]);

        if (response == 'I')
        {
        // Insert an phone book entry into the linked list.
            // Maintain the list in alphabetical order by family name.
            //   ADD STATEMENT(S) HERE
        // USE THE FOLLOWING PRINTF STATEMENTS WHEN PROMPTING FOR DATA:
            printf("  family name: ");
        fgets(familyName, MAX_LENGTH+1, stdin);
            printf("  first name: ");
        fgets(firstName, MAX_LENGTH+1, stdin);
            printf("  address: ");
        fgets(address, MAX_LENGTH+1, stdin);
            printf("  phone number: ");
        fgets(familyName, MAX_LENGTH+1, stdin);
        newContact(familyName, firstName, address, phoneNumber, &head);
        }
        else if (response == 'D')
        {
            // Delete an phone book entry from the list.
            //   ADD STATEMENT(S) HERE

            printf("\nEnter family name for entry to delete: ");
        fgets(res,MAX_LENGTH,stdin);
        contactDel(&head, res);

        }
        else if (response == 'S')
        {
            // Search for an phone book entry by family name.

            printf("\nEnter family name to search for: ");
        fgets(familyName, MAX_LENGTH, stdin);
            temp=contact_search(head, familyName);
            if (temp == NULL)
            familyNameFound(res);
            else
        {
            printf("%s\n%s\n%s\n%s\n\n", temp->familyName, temp->firstName, temp->address, temp->phoneNumber);
        }
            //   ADD STATEMENT(S) HERE

        }
        else if (response == 'R')
        {
            // Search for an phone book entry by phone number.
            //ADD STATEMENT(S) HERE
            printf("\nEnter phone number to search for: ");
            fgets(phoneNumber,MAX_LENGTH,stdin);
            temp = phone_search(head, phoneNumber);
            if (temp==NULL)
                phoneNumberNotFound(phoneNumber);
            else
            {
                phoneNumberFound(phoneNumber);
                printf("\n%s\n%s\n%s\n%s\n", temp->familyName, temp->firstName, temp->address, temp->phoneNumber);
            }
        }
        else if (response == 'P')
        {
            // Print the phone book.
            //   ADD STATEMENT(S) HERE
            print_all(head);
        }
        else if (response == 'Q')
        {
            ;// do nothing, we'll catch this below
        }
        else
        {
            // do this if no command matched ...
            printf("\nInvalid command.\n%s\n",commandList);
        }
    } while (response != 'Q');

    // Delete the whole phone book linked list.
    //   ADD STATEMENT(S) HERE
    DelAll(&head);
    // Print the linked list to confirm deletion.
    //   ADD STATEMENT(S) HERE
    print_all (head);
    return 0;
}

//**********************************************************************
// Support Function Definitions

// Function to get a line of input without overflowing target char array.
void safegets (char s[], int arraySize)
{
    int i = 0, maxIndex = arraySize-1;
    char c;
    while (i < maxIndex && (c = getchar()) != NEWLINE)
    {
        s[i] = c;
        i++;
    }
    s[i] = NULL_CHAR;
}

// Function to call when user is trying to insert a family name
// that is already in the book.
void familyNameDuplicate (char familyName[])
{
    printf("\nAn entry for <%s> is already in the phone book!\n"
           "New entry not entered.\n",familyName);
}

// Function to call when a family name was found in the phone book.
void familyNameFound (char familyName[])
{
    printf("\nThe family name <%s> was found in the phone book.\n",
             familyName);
}

// Function to call when a family name was not found in the phone book.
void familyNameNotFound (char familyName[])
{
    printf("\nThe family name <%s> is not in the phone book.\n",
             familyName);
}

// Function to call when a family name that is to be deleted
// was found in the phone book.
void familyNameDeleted (char familyName[])
{
    printf("\nDeleting entry for family name <%s> from the phone book.\n",
             familyName);
}

// Function to call when a phone number was found in the phone book.
void phoneNumberFound (char phoneNumber[])
{
    printf("\nThe phone number <%s> was found in the phone book.\n",
             phoneNumber);
}

// Function to call when a phone number was not found in the phone book.
void phoneNumberNotFound (char phoneNumber[])
{
    printf("\nThe phone number <%s> is not in the phone book.\n",
             phoneNumber);
}

// Function to call when printing an empty phone book.
void printPhoneBookEmpty (void)
{
    printf("\nThe phone book is empty.\n");
}

// Function to call to print title when whole phone book being printed.
void printPhoneBookTitle (void)
{
    printf("\nMy Personal Phone Book: \n");
}

//**********************************************************************
// Add your functions below this line.
//   ADD STATEMENT(S) HERE
void newContact (char familyName[], char firstName[], char address[], char phoneNumber[], contact **head)
{
    contact *testPtr=NULL;
    testPtr = contact_search (*head, familyName);
    if (strcmp(testPtr->familyName, familyName)==0)
    {
        familyNameDuplicate(familyName);
        return;
    }
    else
        testPtr = *head;
    for ( ; strcmp(testPtr->familyName, familyName) < 0; testPtr = testPtr -> link)
    ;
    contact *c = (contact *)malloc (sizeof(contact));//new node
    strcpy(c->familyName, familyName);
    strcpy(c->firstName, firstName);
    strcpy(c->address, address);
    strcpy(c->phoneNumber, phoneNumber);
    if (head==NULL)
    {
        c->link = *head;
        *head=c;
    }
    else
    {
        ;
    }
}

void contactDel (contact **p, char familyName[]) /* remove head */
{
    contact *control=contact_search(*p, familyName);
    if (control != NULL)
    {
        contact *n = control;
        control = control -> link;
        free(n);
    }
}

void DelAll (contact **p)
{
    if (*p != NULL)
    {
        contact *n = *p;
        *p = (*p)->link;
        free(n);
    }
    return;
}

contact *contact_search (contact *c, char familyName[]) /* By Family Name*/
{
    while (c != NULL)
    {
        if (strcmp(c->familyName, familyName)==0)
        {
            return c;
        }
        c = c->link;
    }
    return NULL;
}

contact *phone_search (contact *c, char phoneNumber[])
{
    while (c != NULL)
    {
        if (strcmp(c->phoneNumber, phoneNumber)==0)
        {
            return c;
        }
        c = c->link;
    }
    return NULL;
}

void print_all (contact *head)
{
    if (head == NULL)
    {
        printPhoneBookEmpty();
    }
    else
    {
            printPhoneBookTitle();
        while (head != NULL)
            {
                printf("%s\n%s\n%s\n%s\n\n", head->familyName, head->firstName, head->address, head->phoneNumber);
            head = head->link;
            }
    }
}

the program should keep the inputs in the list and then print them after it gets the option from the user. and so on but why am i getting the segmentation failure?

  • 2
    Do you know about debuggers? They allow one to single-step a program so that it becomes obvious which line is causing the problem. Then you can evaluate all the parts of the troublesome statement's expressions to figure out what is wrong. – wallyk Mar 31 '18 at 22:36
  • "`safegets`" already exists.. it's [`fgets`](https://linux.die.net/man/3/fgets) – yano Mar 31 '18 at 22:37
  • Yes, you should "learn to fish" (use your debugger) then you can feed yourself working code for a lifetime :) – Dave S Mar 31 '18 at 22:38
  • Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Complete, and Verifiable example. – unalignedmemoryaccess Mar 31 '18 at 22:45
  • 1
    Please show, in the question, example input, expected output and actual output. – Weather Vane Mar 31 '18 at 22:59
  • `4004 bytes` per node? Wow, 100k of contacts with is of `400M` of storage - yikes. You must be working with Mozilla? – David C. Rankin Mar 31 '18 at 23:20
  • Possible duplicate of [What is a segmentation fault?](https://stackoverflow.com/questions/2346806/what-is-a-segmentation-fault) – Marco Mar 31 '18 at 23:58

2 Answers2

2

In addition to the testPtr being NULL above, you will run into the problem again with:

for ( ; strcmp(testPtr->familyName, familyName) < 0; testPtr = testPtr -> link)
    ;

If testPtr is null, you cannot call strcmp(testPtr->familyName and you cannot do testPtr = testPtr -> link. (this is your last SegFault on insert)

(note: instead of a trailing ;, it is much easier to read if you use an empty set of braces instead, e.g. for (...) {})

However, when you Search (S), you will never match testPtr->familyName because after reading with fgets, your familyName contains:

+---+---+---+---+---+---+---+---+---+
| S | o | m | e | n | a | m | e | \n|  <== note trailing '\n' included
+---+---+---+---+---+---+---+---+---+

In order to trim the trailing newline, either use:

        if (!fgets(familyName, MAX_LENGTH, stdin)) {
            fprintf (stderr, "error: user canceled input.\n");
            continue;
        }
        size_t len = strlen (familyName);        /* get length */
        if (len && familyName[len - 1] == '\n')  /* check last char '\n' */
            familyName[--len] = 0;               /* overwrite with nul-char */
        else {
            /* handle line too long */
        }

You will need to trim the trailing newline on each of your uses of fgets and you should handle the "Line Too Long" case as well. You can also use any of the other string function to locate the trailing '\n', e.g. char *nl = strchr (familyName, '\n'); if (nl) *nl = 0;, etc.. Creating a short function to handle this chore will save a significant amount of code-duplication.

Even making those changes, your Search (S) still reports an empty list -- but your SegFault is remedied. Solving that issue is left to you.

Also, after you allocate for

contact *c = (contact *)malloc (sizeof(contact));//new node

You must validate (c != NULL) as malloc can fail, or you can exhaust the memory available (which is increasingly likely allocating 10*2 bytes for every string in contact). Also see: Do I cast the result of malloc?

To moderate your memory use, you should allocate for familyName, firstName, address and phoneNumber. strdup provides a simple manner of doing do. Simply declare each of your strings in contacts as a pointer, and then after reading, e.g. familyName with fgets and trimming the newline, simply allow strdup to allocate for the exact space needed and copy familyName to the new block of memory. It's then a simple matter of

c->familyName = strdup (familyName);

Since strdup allocates, you should also validate it succeeded, e.g.

if (!c->familyName) {
    /* handle error */
}

Let me know if you run into any other hurdles you can't get over and I'm happy to help further.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

The first time you call newContact the linked list is empty and this causes a null pointer dereference on this line:

if (strcmp(testPtr->familyName, familyName)==0)

That's because contactSearch on the previous line returned null and so testPtr is null. You want to check if testPtr is null before referencing it.

This is a great opportunity to learn about gdb and practice using it! It's a very valuable skill to develop.

bchurchill
  • 1,410
  • 8
  • 23