0

I've written a program to read a set of data from a file into a linked list. The program reads a line from the file, tokenizes the line, stores it into a node and then moves on to the next line. Each time it does this, I print the contents of node. The issue I'm having is that when the program is run, it stops at one of two lines in the file and crashes. It stops at one line more frequently than the other.

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

typedef struct prefectNode
{
    char *name; // prefect name
    char *type; // prefect type
    char *used;
    struct free// structure storing the prefect's free sessions on particular days
    {
        char M[9];
        char T[9];
        char W[9];
        char S[9];
        char F[9];
    } fses; // free sessions available to prefect;
    struct prefectNode *nextPtr;// a pointer to the next node
} pNode;// Self-Referential structure used to store information about a prefect
typedef pNode *ptrNode;// Synonym for a pointer to a self-referential structure

void induct(ptrNode *list, char l[]);// induct is called when information is being retrieved from file.

void induct(ptrNode *list, char l[])
{
    ptrNode curPtr = NULL;// pointer to the current node

    ptrNode newPtr = malloc(sizeof(pNode));// creating new node

    if (newPtr != NULL)
    {
        newPtr->name = strtok(l, ",");

        newPtr->type = strtok(NULL, ",");

        char *sesh = strtok(NULL, " ");
        char c = sesh[0];
        printf("%s\n%s\n", newPtr->name, newPtr->type);

        int i = 0, j = 0;
        for (i = 0; i < strlen(sesh); i++)
        {
            char c = sesh[i];
            if (c == 'M')
            {
                newPtr->fses.M[j] = sesh[i + 1];
                printf("M%c", newPtr->fses.M[j]);
            }
            if (c == 'T')
            {
                newPtr->fses.T[j] = sesh[i + 1];
                printf("T%c", newPtr->fses.T[j]);
            }
            if (c == 'W')
            {
                newPtr->fses.W[j] = sesh[i + 1];
                printf("W%c", newPtr->fses.W[j]);
            }
            if (c == 'S')
            {
                newPtr->fses.S[j] = sesh[i + 1];
                printf("S%c", newPtr->fses.S[j]);
            }
            if (c == 'F')
            {
                newPtr->fses.F[j] = sesh[i + 1];
                printf("F%c", newPtr->fses.F[j]);
            }

            i = i + 2;
            j++;
        }

        newPtr->nextPtr = NULL;
        curPtr = *list;

        if (curPtr == NULL)
        {
            newPtr->nextPtr = *list;
            *list = newPtr;
        }
        else
        {
            while (curPtr->nextPtr != NULL)
            {
                curPtr = curPtr->nextPtr;
            }
            curPtr->nextPtr = newPtr;
        }
    }
    else
    {
        printf("no memory");
    }
}

char ln[200];
FILE *fp;
int main()
{
    ptrNode start = NULL;

    fp = fopen("thefile.txt", "r");

    if (fp == NULL)
    {
        printf("File not found.");
    }
    else
    {
        while (!feof(fp))
        {
            fgets(ln, sizeof(ln), fp);
            induct(&start, ln);
            printf("\n\n");
        }
    }
    fclose(fp);
    getch();
    return 0;
}

This is a sample of what the input file looks like:

Denelia Alvaranga,R,M7_T4_T5_T6
Sainna Christian,R,M4_M5_M6_T1_T2_T3_F5_F6
Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7
Candice Gordon,R,M1_M2_M3_S3_T7
Alphene Groves,R,M2_T5_S7_W3
T-Anna Johnson,R,M7
Vanessa Lewis,R,M1_M2_M3_S3_T4_W4_S4_T5_S5_T6_S6

Program crashes (more frequently) when I get to line 9:

Rhoni-Ann Parkins,R,W1_S1_W2_S2_W3_S3_M4_T4_S4_F4_M5_T5_F5_M6_T6_F6_T7_F7

or line 37:

Ashleigh Graham,N6A 
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jourdan
  • 3
  • 3

1 Answers1

0

Principal Problem

One of your principal problems is that j is out of control — it is never zeroed after it is first initialized. Rhoni-Ann causes trouble because she has lots of free periods; Ashleigh causes trouble because she has no free periods. With Rhoni-Ann, j gets to 18, but the arrays you're indexing only go up to 9. You need to revisit that whole portion of the code. You need to read the number after the day letter and use that in place of the j value. You should also validate that the value is small enough (a value of 9 would cause overflows again; only 0..8 is OK), and report if the day letter is not valid. Note too that the space is not zeroed when the memory is allocated. It is not clear what you're going to do afterwards, but you don't have valid strings in the M, T, etc arrays.

Revised code

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

typedef struct prefectNode
{
    char *name; // prefect name
    char *type; // prefect type
    char *used;
    struct free// structure storing the prefect's free sessions on particular days
    {
        char M[9];
        char T[9];
        char W[9];
        char S[9];
        char F[9];
    } fses; // free sessions available to prefect;
    struct prefectNode *nextPtr;// a pointer to the next node
} pNode;// Self-Referential structure used to store information about a prefect
typedef pNode *ptrNode;// Synonym for a pointer to a self-referential structure

void induct(ptrNode *list, char l[]);// induct is called when information is being retrieved from file.

void induct(ptrNode *list, char l[])
{
    ptrNode curPtr = *list; // pointer to the current node
    ptrNode newPtr = calloc(sizeof(*newPtr), 1);// creating new node

    if (newPtr != NULL)
    {
        newPtr->name = strdup(strtok(l, ","));
        newPtr->type = strdup(strtok(NULL, ","));

        printf("Prefect: %s, type %s\n", newPtr->name, newPtr->type);

        char *sesh = strtok(NULL, " ");
        if (sesh == NULL)
        {
            fprintf(stderr, "No free sessions for %s\n", newPtr->name);
            free(newPtr->name);
            free(newPtr->type);
            free(newPtr);
            return;
        }

        int i = 0, j = 0;
        int len = strlen(sesh);
        for (i = 0; i < len; i++)
        {
            char c = sesh[i];
            if (c == 'M')
            {
                newPtr->fses.M[j] = sesh[i + 1];
                printf("M%c", newPtr->fses.M[j]);
            }
            if (c == 'T')
            {
                newPtr->fses.T[j] = sesh[i + 1];
                printf("T%c", newPtr->fses.T[j]);
            }
            if (c == 'W')
            {
                newPtr->fses.W[j] = sesh[i + 1];
                printf("W%c", newPtr->fses.W[j]);
            }
            if (c == 'S')
            {
                newPtr->fses.S[j] = sesh[i + 1];
                printf("S%c", newPtr->fses.S[j]);
            }
            if (c == 'F')
            {
                newPtr->fses.F[j] = sesh[i + 1];
                printf("F%c", newPtr->fses.F[j]);
            }
            printf("(j=%d)\n", j);

            i = i + 2;
            j++;
        }

        newPtr->nextPtr = NULL;
        curPtr = *list;

        if (curPtr == NULL)
        {
            newPtr->nextPtr = *list;
            *list = newPtr;
        }
        else
        {
            while (curPtr->nextPtr != NULL)
            {
                curPtr = curPtr->nextPtr;
            }
            curPtr->nextPtr = newPtr;
        }
    }
    else
    {
        printf("no memory\n");
    }
}

static void free_node(pNode *node)
{
    if (node != 0)
    {
        free(node->name);
        free(node->type);
        free(node);
    }
}

static void free_list(pNode *node)
{
    while (node != 0)
    {
        pNode *next = node->nextPtr;
        free_node(node);
        node = next;
    }
}

static void print_sessions(const char *dow, const char *ses)
{
    printf(" %s:", dow);
    for (int i = 0; i < 9; i++)
    {
        if (ses[i] != '\0')
            printf(" %c", ses[i]);
    }
}

static void print_node(const pNode *node)
{
    printf("%s [%s]", node->name, node->type);
    print_sessions("Mon", node->fses.M);
    print_sessions("Tue", node->fses.T);
    print_sessions("Wed", node->fses.W);
    print_sessions("Thu", node->fses.S);
    print_sessions("Fri", node->fses.F);
    putchar('\n');
}

static void print_list(const char *tag, pNode *list)
{
    printf("%s:\n", tag);
    while (list != NULL)
    {
        print_node(list);
        list = list->nextPtr;
    }
}

int main(void)
{
    FILE *fp;
    char ln[200];
    ptrNode start = NULL;
    const char filename[] = "thefile.txt";

    fp = fopen(filename, "r");

    if (fp == NULL)
    {
        fprintf(stderr, "File %s not found.\n", filename);
    }
    else
    {
        while (fgets(ln, sizeof(ln), fp) != 0)
        {
            size_t len = strlen(ln);
            if (len > 0)
                ln[len-1] = '\0';
            printf("[%s]\n", ln);
            induct(&start, ln);
        }
        fclose(fp);
        print_list("After data read", start);
        free_list(start);
    }

    return 0;
}

Sample run under valgrind

==54054== Memcheck, a memory error detector
==54054== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==54054== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==54054== Command: xyz
==54054== 
[Denelia Alvaranga,R,M7_T4_T5_T6]
Prefect: Denelia Alvaranga, type R
M7(j=0)
T4(j=1)
T5(j=2)
T6(j=3)
[Sainna Christian,R,M4_M5_M6_T1_T2_T3_F5_F6]
Prefect: Sainna Christian, type R
M4(j=0)
M5(j=1)
M6(j=2)
T1(j=3)
T2(j=4)
T3(j=5)
F5(j=6)
F6(j=7)
[Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7]
Prefect: Kashielle Clarke, type R
F1(j=0)
T4(j=1)
W4(j=2)
F4(j=3)
T5(j=4)
W5(j=5)
S5(j=6)
W6(j=7)
S6(j=8)
M7(j=9)
W7(j=10)
[Candice Gordon,R,M1_M2_M3_S3_T7]
Prefect: Candice Gordon, type R
M1(j=0)
M2(j=1)
M3(j=2)
S3(j=3)
T7(j=4)
[Alphene Groves,R,M2_T5_S7_W3]
Prefect: Alphene Groves, type R
M2(j=0)
T5(j=1)
S7(j=2)
W3(j=3)
[T-Anna Johnson,R,M7]
Prefect: T-Anna Johnson, type R
M7(j=0)
[Vanessa Lewis,R,M1_M2_M3_S3_T4_W4_S4_T5_S5_T6_S6]
Prefect: Vanessa Lewis, type R
M1(j=0)
M2(j=1)
M3(j=2)
S3(j=3)
T4(j=4)
W4(j=5)
S4(j=6)
T5(j=7)
S5(j=8)
T6(j=9)
S6(j=10)
[Rhoni-Ann Parkins,R,W1_S1_W2_S2_W3_S3_M4_T4_S4_F4_M5_T5_F5_M6_T6_F6_T7_F7]
Prefect: Rhoni-Ann Parkins, type R
W1(j=0)
S1(j=1)
W2(j=2)
S2(j=3)
W3(j=4)
S3(j=5)
M4(j=6)
T4(j=7)
S4(j=8)
F4(j=9)
M5(j=10)
T5(j=11)
F5(j=12)
M6(j=13)
T6(j=14)
F6(j=15)
T7(j=16)
F7(j=17)
[Ashleigh Graham,N6A ]
Prefect: Ashleigh Graham, type N6A 
No free sessions for Ashleigh Graham
After data read:
Denelia Alvaranga [R] Mon: 7 Tue: 4 5 6 Wed: Thu: Fri:
Sainna Christian [R] Mon: 4 5 6 Tue: 1 2 3 Wed: Thu: Fri: 5 6
Kashielle Clarke [R] Mon: Tue: 7 4 5 Wed: 4 5 6 Thu: 7 5 6 Fri: 1 4
Candice Gordon [R] Mon: 1 2 3 Tue: 7 Wed: Thu: 3 Fri:
Alphene Groves [R] Mon: 2 Tue: 5 Wed: 3 Thu: 7 Fri:
T-Anna Johnson [R] Mon: 7 Tue: Wed: Thu: Fri:
Vanessa Lewis [R] Mon: 1 2 3 Tue: 4 5 Wed: 6 4 Thu: 3 4 5 Fri: 6
Rhoni-Ann Parkins [R] Mon: 4 Tue: 5 6 4 Wed: 1 5 3 6 7 Thu: 1 2 3 4 Fri:
==54054== 
==54054== HEAP SUMMARY:
==54054==     in use at exit: 26,297 bytes in 186 blocks
==54054==   total heap usage: 297 allocs, 111 frees, 37,503 bytes allocated
==54054== 
==54054== LEAK SUMMARY:
==54054==    definitely lost: 0 bytes in 0 blocks
==54054==    indirectly lost: 0 bytes in 0 blocks
==54054==      possibly lost: 0 bytes in 0 blocks
==54054==    still reachable: 0 bytes in 0 blocks
==54054==         suppressed: 26,297 bytes in 186 blocks
==54054== 
==54054== For counts of detected and suppressed errors, rerun with: -v
==54054== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

The 'suppressed' memory is an artefact of running on Mac OS X (10.11.3). I'm using GCC 5.3.0. The code is clean — it releases all the memory it allocates. (That's what the zeros for 'definitely lost', 'indirectly lost', 'possibly lost', and 'still reachable' mean.)

Note that despite using valgrind, it is not complaining about 'out of bounds' memory accesses, but you can see that j is printed and reaches 'j=17' for Rhoni-Ann. You're definitely not writing where you intended — the output for each prefect does not match your input, because j is out of control. It so happens that when you write to newPtr->fses.F[j] with j set to 17 on a 64-bit system, you are still within bounds of the memory allocated because the struct free occupies 45 bytes, but is padded to 48 bytes to align the 8-byte pointer nextPtr, so F[17] is within the pointer (which you subsequently over-write). If you are compiling with a 32-bit compiler, the story might be different. (I got some 'Conditional jump or move depends on uninitialised value(s)' errors, but not an 'out of bounds' write as I expected.)

Other problems:

  • See [while (!feof(file)) is always wrong] for why your input loop is flawed.
  • You shouldn't try to close the file if you failed to open it.
  • You should report errors to stderr, not stdout.
  • You should end messages with newlines so that they're seen.
  • Both fp and ln should be local variables in main.
  • You overwrite the names you store because you don't make a copy of the strings for inclusion in the structure — they all point to the array ln.
  • You don't set the used element to a known value.
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • thanks for the response, free period arrays are 9 for a particular day, as one day is split into 9 sessions. No one has 18 free periods for one day, so i dont thats the issie. – Jourdan Mar 13 '16 at 00:48
  • think thats the issue* – Jourdan Mar 13 '16 at 00:49
  • Your variable `j` is most definitely out of control; see the demonstration above. It may or may not cause your program to crash; with the system I tested, the writes were all still within bounds of the data structure — just — and therefore didn't trigger an error from `valgrind`. But by moving the `nextPtr` before the `struct free` (and removing `used`) gets errors about `Invalid write of size 1` and `Address 0x100aa8db8 is 0 bytes after a block of size 72 alloc'd` which is an out of bounds write problem. – Jonathan Leffler Mar 13 '16 at 01:25
  • If you use `print_node()` in the `induct()` function after processing the data (before hooking the new node into the list), you can easily see that the data generated for a given prefect does not necessarily match what was entered. The output for Denelia and Sainna happens to appear 'correct' (though I'd argue that it is only coincidentally correct — a revised `print_session()` would demonstrate), but the output for Kashielle is wrong: `[Kashielle Clarke,R,F1_T4_W4_F4_T5_W5_S5_W6_S6_M7_W7] Prefect: Kashielle Clarke, type R … Kashielle Clarke [R] Mon: Tue: 7 4 5 Wed: 4 5 6 Thu: 7 5 6 Fri: 1 4` – Jonathan Leffler Mar 13 '16 at 01:34
  • Thanks do much Jonathan. My code works now and your answer taught me a whole lot! – Jourdan Mar 13 '16 at 18:11