2

I have written a code to enter the name, age, department id, company name, and salary respectively, of employees from a text file into a linked list. Right now, In my code, I have created an insert function and a display function. When I ran the code on the command prompt there were no errors found. However, my list did not get printed. Can someone help me out with this if possible? thankyou.

Here are the details of the file, it is called employee.txt:

Peter 30 1001 Apple 8000
Joseph 50 1002 Oracle 4000
Mary 40 1003 Samsung 6000
Lilly 40 1203 Samsung 7000
Tony 50 1002 Oracle 3000
Jake 30 1005 Apple 3000
Sam 40 1007 Samsung 4000
Lisa 30 1300 Oracle 5000
Kate 50 1200 Apple 6000
Rick 50 1313 Apple 4000

My Code:

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

struct personTag {
    char name[20];
    int age;
};

struct officialTag {
    int deptId;
    char cmpName[20];
    double salary;
};

struct employeeTag {
    struct personTag personalInfo;
    struct officialTag officialInfo;
    struct employeeTag *next;
};

typedef struct employeeTag EmpTag;
typedef EmpTag *EmpTagPtr;

void insert(EmpTagPtr *s, char E_name[], int E_age, int E_deptid,
            char E_cmpname[], double E_salary);

void displayEmployees(EmpTagPtr s);

int main() {
    EmpTagPtr start = NULL;

    char E_name[20];
    int E_age;
    int E_deptid;
    char E_cmpname[20];
    double E_salary;

    // reading employee.txt file
    FILE *fp;
    fp = fopen("employee.txt", "r");

    fscanf(fp, "%s,%d,%d,%s,%lf", E_name, &E_age, &E_deptid, E_cmpname,
           &E_salary);

    while (!feof(fp)) {
        insert(&start, E_name, E_age, E_deptid, E_cmpname,
               E_salary);  // inserting data to the new node
        fscanf(fp, "%s,%d,%d,%s,%lf", E_name, &E_age, &E_deptid, E_cmpname,
               &E_salary);
    }

    fclose(fp);

    displayEmployees(start);

    return 0;
}

void insert(EmpTagPtr *s, char E_name[], int E_age, int E_deptid,
            char E_cmpname[], double E_salary) {
    EmpTagPtr current = *s;
    EmpTagPtr prev = NULL;

    // create an empty node
    EmpTagPtr newNode;
    newNode = (EmpTag *)malloc(sizeof(EmpTag));

    // filling in the values
    strcpy(newNode->personalInfo.name, E_name);
    newNode->personalInfo.age = E_age;

    newNode->officialInfo.deptId = E_deptid;
    strcpy(newNode->officialInfo.cmpName, E_cmpname);
    newNode->officialInfo.salary = E_salary;

    newNode->next = NULL;

    while (current != NULL &&
           strcmp(current->personalInfo.name, prev->personalInfo.name) > 0) {
        prev = current;
        current = current->next;
    }

    if (prev == NULL) {
        // add as the first node
        newNode->next = *s;
        *s = newNode;
    } else {
        prev->next = newNode;
        newNode->next = current;
    }
}

void displayEmployees(EmpTagPtr s) {
    EmpTagPtr current = s;

    while (current != NULL) {
        // printing the data part
        printf("Employee name:    %s\n", current->personalInfo.name);
        printf("Company name:     %s\n", current->officialInfo.cmpName);
        printf("Employee Age:     %d\n", current->personalInfo.age);
        printf("Department ID:    %d\n", current->officialInfo.deptId);
        printf("Employee Salary:  %lf\n", current->officialInfo.salary);
        printf("---------------------------------------------------------");
        current = current->next;  // move foward the current pointer
    }

    printf("NULL\n");
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • For me it segfaults in insert. – Allan Wind Aug 02 '22 at 18:42
  • Was reading it and was just going to point this out. start == NULL. First time insert is called we are then access *s (that is *start) which is NULL so you should get a NULL pointer access. On Windows this will simply look like the app runs and does nothing. You need to check s and make sure it is not NULL before you access it. – Chris Aug 02 '22 at 18:45
  • I fixed your data to remove the extra new lines. Your fscanf comma separates fields but your data is space separated. Which is it? – Allan Wind Aug 02 '22 at 18:46
  • See [Why is “while( !feof(file) )” always wrong?](https://stackoverflow.com/q/5431941/2472827) Also, your file is not separated with commas, check the return value of `fscanf`. – Neil Aug 02 '22 at 19:00
  • That part is fine. start is basically a employeeTag**, so therefore *s is expected to get the first employeeTag*, however there is no first employeeTag. – Chris Aug 02 '22 at 19:00

3 Answers3

2

Your loop to read uses the wrong fscanf format. The loop is also a bit overcomplicated. Just fscanf and check that you successfully extracted the 5 elements. Note the limits for the strings and removed commas:

while (fscanf(fp, "%19s %d %d %19s %lf", E_name, &E_age, &E_deptid,
              E_cmpname, &E_salary) == 5)
{
    insert(&start, E_name, E_age, E_deptid, E_cmpname, E_salary);
}

The insert function has a buggy loop to find the insertion point. prev will be a NULL pointer here:

while(current != NULL && strcmp(current->personalInfo.name,
      prev->personalInfo.name) >0 )
{
    prev = current;
    current = current->next;
}

Suggested fix:

void insert(EmpTagPtr *s, char E_name[], int E_age, int E_deptid,
            char E_cmpname[], double E_salary) 
{
    // create an empty node
    EmpTagPtr newNode = malloc(sizeof *newNode);

    // filling in the values
    strcpy(newNode->personalInfo.name, E_name);
    newNode->personalInfo.age = E_age;

    newNode->officialInfo.deptId = E_deptid;
    strcpy(newNode->officialInfo.cmpName, E_cmpname);
    newNode->officialInfo.salary = E_salary;

    // use `newNode` in the comparison:
    while (*s && strcmp(newNode->personalInfo.name,
                        (*s)->personalInfo.name) > 0)
    {
        s = &(*s)->next;
    }

    // `s` now points at the `EmpTagPtr` pointer where the new node should
    // be inserted for the list to be sorted in alphabetical order.
    // If it's the first node to be inserted, `s` points at `start` so
    // you do not need `prev` or `current`:
    newNode->next = *s;
    *s = newNode;
}

Demo

As can be seen in the demo, your program leaks memory so you need to add a function to clean that up.

More about the insertion logic:

  • Think of start as the next member of an imaginary first dummy node and s starts out pointing at start.
  • If the linked list has zero elements, start will point at NULL.
    • *s will therefore be == NULL and the while loop will not do any laps.
    • You now assign newNode->next = *s (which is NULL) and assign *s = newNode (which means start will point at newNode). The first node is now completely linked.
  • If the linked list has more than zero elements, the while loop will compare the name of the newNode with that of the dereferenced s's name. EmpTagPtr current = *s; followed by current->personalInfo.name is the same as (*s)->personalInfo.name.
    • If strcmp(newNode->personalInfo.name, (*s)->personalInfo.name) > 0 we haven't found the insertion point and step s to point at the next node's next member, s = &(*s)->next - and loop to check the conditions again.
    • If the condition is false it means that newNode should be inserted between the node to which the next member s points at belongs and the *s node - and the loop terminates. When the loop terminates s will therefore point at a next member where the new node should be inserted.

It can be hard to "see" it without actually doing this with pen and paper so I recommend doing just that. Insert 3 nodes using only pen and paper and draw the pointers to see where everything ends up. It usually gives me an "aha!" moment.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • Thank you this solves my problem, however, the demo code does not run on the command prompt for some reason....there are no errors but it doesn't print the output. – on_my_puter Aug 05 '22 at 15:11
  • @on_my_puter You're welcome! In the demo I commented out `fp = fopen("employee.txt", "r");` and replaced it with `fp = stdin;` - so, just do the opposite. Comment out `fp = stdin;` and uncomment the `fopen` line. – Ted Lyngmo Aug 05 '22 at 15:20
  • If its alright could please explainwhat happens in this code please: 'while (*s && strcmp(newNode->personalInfo.name, (*s)->personalInfo.name) > 0) { s = &(*s)->next; } // `s` now points at the `EmpTagPtr` pointer where the new node should be inserted: newNode->next = *s; *s = newNode;' – on_my_puter Aug 05 '22 at 15:38
  • @on_my_puter Sure. I'm at a wedding party right now so I'll do it when things calm down :-) – Ted Lyngmo Aug 05 '22 at 15:40
  • Sure, take your time :) – on_my_puter Aug 05 '22 at 19:51
  • @on_my_puter I added some explanations. I'll clarify it further if there are details you feel I left out. :-) – Ted Lyngmo Aug 05 '22 at 20:20
  • I did it on paper and I understood the linking and dereferencing part, Thank you so much for your help <3 – on_my_puter Aug 06 '22 at 11:48
1

As Allan Wind already commented on your question, the first problem was that you had commas in fscanf even though your data is seperated by spaces.

A bigger problem is that segmentation fault occurs in the following while loop in your insert() function

while(current != NULL && strcmp(current->personalInfo.name , prev->personalInfo.name) >0 )
{
    prev = current;
    current = current->next;
}

This happens because you are accesing personalInfo of prev when prev is set to NULL. I assume that what you wanted to do here is insert an elemente into the linked list so that the list stays sorted alphabetically by names. In that case, you must compare names of current and newNode in strcmp and NOT names of current and prev. Your condition in the while loop should look like this:

while(current != NULL && prev != NULL && strcmp(current->personalInfo.name , newNode->personalInfo.name) > 0 )
  • I just realized that the point of insert is probably to insert it in alphabetical order. Could you confirm if that is the case? I'm editing the answer to fix it – Friendly Giant Aug 02 '22 at 19:06
  • Yes, I want to print the list in alphabetical order. Sorry for the inconvenience. – on_my_puter Aug 02 '22 at 20:10
  • I'm sorry, not printing, I want to insert in alphabetical order. – on_my_puter Aug 02 '22 at 20:16
  • 1
    cool, I already edited my answer so that it is sorted after insertion, but is currently in reverse alphabetical order. To sort alphabetically you can change `strcmp(...) > 0` to `strcmp(...) < 0` – Friendly Giant Aug 02 '22 at 20:21
1

I think an important point to improve your programme is database normalization. You might end up with roughly:

Normalized.

Where, person.txt is (an example),

1 Peter 30
2 Joseph 50
3 Mary 40

works.txt

1 1001 8000
2 1002 4000
3 1002 6000

department.txt

1001 1
1002 1

company.txt

1 Apple

Instead of duplicating data, this reduces data redundancy. This is good for if you want to add or make changes to your database. It also mirrors the usage (somewhat) in your programme.

Neil
  • 1,767
  • 2
  • 16
  • 22