1
votes

My C program should read an input file and save it line for line in a linked list, then printing it. But when it's inserting the next line in the list, all elements in the list become the new element.

Input:

abc
def
ghi

Output:

ghi
ghi
ghi

I'm not sure but my guess is that it's not correctly allocating new memory for new elements in the list.

This is the code:

#include "listd.h"
#include <stdio.h>
void show(Value v) {
    printf(v);
    printf("\n");
}

int main( void ) {
    List lines = init();
    char a;
    while(a != EOF){
        a=getchar();
        char currentline[256] = " ";
        int i = 0;
        while(a != '\n' && a != EOF){
            currentline[i]=a;
            a = getchar();
            i++;
        }
        lines = insert(currentline, lines); //Here is probably the error
    }
    iterate(lines, show);
}

listd.h:

#include <stdlib.h>
#include <stdio.h>
#define DEFAULT 0

typedef enum {False ,True} Bool;
typedef char* Value;
typedef void (*VProc)(Value);
typedef struct _list {
    Value val;
    struct _list *next;
} Elem , *List;

List init() { return NULL; }
List insert(Value v, List l) {
    List e = malloc( sizeof (Elem));
    e->val = v;
    e->next = l;
    return e ;
}
void iterate(List l, VProc p) {
    for (;!empty(l);l=tail(l)) p(head(l));
    }

listd.h was given to us. But it was made for int, so I changed

typedef int Value;

to

typedef char* Value;
2
All data members val of nodes point to the same local array aktzeile. So what you do is what you get. You should dynamically allocate character arrays for data member val and copy strings into them.Vlad from Moscow
You're going to have to duplicate that string or you're stomping the same memory location with each line. strdup() or strncpy() into a new buffer.tadman
@Ben Take into account that this code snippet char a; while(a != EOF){ invokes undefined behavior because the variable a is not initialized and has indeterminate value.Vlad from Moscow
@Vlad Yes, you are right. When I change aktzeile I change the output. Thanks for your help, I see if I can solve the problem now.Ben
@Ben, just a tip, while not an error, the standard coding style for C avoids the use of camelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It's style, so it up to you, but when you fail to follow it, it says a lot about your code before you even get to the details :)David C. Rankin

2 Answers

0
votes

Issue1: In struct _list, val is a char pointer. When you do malloc( sizeof (Elem)), only memory for the two pointers(char*, struct_list*) is allocated.

Issue2: When e->val = v is executed it copies the char* v to e->val. It does not copy the string but only pointer. Here v is currentline. So it is obvious that e->val for all the elements will be the pointer to the last value which is stored in currentline. In this case, the last value is "ghi".

Please try below code and let me know:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT 0

typedef enum {False ,True} Bool;
typedef char* Value;
typedef void (*VProc)(Value);
typedef struct _list {
    Value val;
    struct _list *next;
} Elem , *List;

List init() { return NULL; }
List insert(Value v, List l) {
    List e = malloc( sizeof (Elem));
    e->val = malloc(strlen(v) * sizeof(char));
    strcpy(e->val, v);
    e->next = l;
    return e ;
}
0
votes

You program is having undefined behavior as you are accessing address of local variable outside of its scope.

The scope of currentline is within the while loop - while(a != EOF) as it has been declared inside the loop block.

In insert(), you are doing:

e->val = v;

The val pointer (type char *) of each element of list lines is pointing to currentline and currentline does not exist out of while loop block. Your program accessing the list lines outside the while loop block as well which is leading to undefined behavior.

From C Standards:

The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

An undefined behavior includes - program may work as expected or it may crash or anything can happen. As the val pointer of all the elements of the list is pointing to the same memory location which is currentline, the last value of currentline is the value you are getting when you are printing the value pointer val of all elements of list lines pointing to. You are lucky that you are not getting any error while your program is accessing object memory outside of its scope. But this may not be the case every time when you run your program.

To fix this problem, you should allocate memory to val pointer and copy the value currentline to it.

You can do:

List insert(Value v, List l) {
    List e = malloc(sizeof(Elem));
    if (NULL == e)
        exit(EXIT_FAILURE);

    e->val = malloc((strlen(v)+1) * sizeof(char));
    if (NULL == e->val)
        exit(EXIT_FAILURE);

    strcpy(e->val, v);
    e->next = l;
    return e ;
}