Why can I not add to a linked list correctly when using a function?

只愿长相守 提交于 2020-01-05 12:31:20

问题


I am trying to a create a linked list. When I add to the list in the same function that I create the object in, it works.

Definitions:

typedef struct student {
    int num;
    char* name;
} student;

typedef struct container {
    student* data;
    struct container* next;
} container ;

All the objects I use are initialized like this:

student stu1;
stu1.num = 6;
stu1.name = "grefagf";

front = createContainer(&stu1);
back = front;

student stu2;
stu2.num = 3;
stu2.name = "dsghjyreawre";

student stu3;
stu3.num = 4;
stu3.name = "dsghhjrant";

student stu4;
stu4.num = 213;
stu4.name = "fdsafgrw";

When I add these elements to a list in the main function like this:

container* tmp;

tmp = createContainer(&stu2);
back->next = tmp;
back = tmp;

tmp = createContainer(&stu3);
back->next = tmp;
back = tmp;

tmp = createContainer(&stu4);
back->next = tmp;
back = tmp;

it works properly, outputting this:

1:  6       grefagf
2:  3       dsghjyreawre
3:  4       dsghhjrant
4:  213     fdsafgrw

when I print it using another function I made.

But if I create a function called add() and pass stu2, stu3..., like so:

int add(student to_add) {

    container* tmp;
    tmp = createContainer(&to_add);
    printf("added: (%d, %s)\n", tmp->data->num, tmp->data->name);

    back->next = tmp;
    back = tmp;

    return 1;
}

then do this in the main function:

add(stu2);
add(stu3);
add(stu4);

it outputs this:

1:  6       grefagf
2:  41096808        fdsafgrw
3:  41096808        fdsafgrw
4:  41096808        fdsafgrw

Heres the source in case you need it:

Non-function example: https://pastebin.com/ZLqTzp4t

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

typedef struct student {
    int num;
    char* name;
} student;

typedef struct container {
    student* data;
    struct container* next;
} container ;

container* back;
container* front;

container* createContainer(student* data) {

    container* tmp = malloc(sizeof(container));

    tmp->data = data;
    tmp->next = NULL;

    return tmp;
}

void printList(container* front) {

    container* tmp = front;

    int i;
    i=0;
    while (tmp != NULL) {
        i++;
        printf("%d:\t%d\t\t%s\n", i, tmp->data->num, tmp->data->name);
        tmp = tmp->next;
    }
}

int main(void) {

    student stu1;
    stu1.num = 6;
    stu1.name = "grefagf";

    front = createContainer(&stu1);
    back = front;

    student stu2;
    stu2.num = 3;
    stu2.name = "dsghjyreawre";

    student stu3;
    stu3.num = 4;
    stu3.name = "dsghhjrant";

    student stu4;
    stu4.num = 213;
    stu4.name = "fdsafgrw";

    container* tmp;

    tmp = createContainer(&stu2);
    back->next = tmp;
    back = tmp;

    tmp = createContainer(&stu3);
    back->next = tmp;
    back = tmp;

    tmp = createContainer(&stu4);
    back->next = tmp;
    back = tmp;

    printf("front\n");
    printList(front);
    printf("\ntop\n");
    printList(back);

    return EXIT_SUCCESS;
}

Function example: https://pastebin.com/TyQY4j5k

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

typedef struct student {
    int num;
    char* name;
} student;

typedef struct container {
    student* data;
    struct container* next;
} container ;

container* back;
container* front;

container* createContainer(student* data) {

    container* tmp = malloc(sizeof(container));

    tmp->data = data;
    tmp->next = NULL;

    return tmp;
}

int add(student to_add) {

    container* tmp;
    tmp = createContainer(&to_add);
    printf("added: (%d, %s)\n", tmp->data->num, tmp->data->name);

    back->next = tmp;
    back = tmp;

    return 1;
}

void printList(container* front) {

    container* tmp = front;

    int i;
    i=0;
    while (tmp != NULL) {
        i++;
        printf("%d:\t%d\t\t%s\n", i, tmp->data->num, tmp->data->name);
        tmp = tmp->next;
    }
}

int main(void) {

    student stu1;
    stu1.num = 6;
    stu1.name = "grefagf";

    front = createContainer(&stu1);
    back = front;

    student stu2;
    stu2.num = 3;
    stu2.name = "dsghjyreawre";

    student stu3;
    stu3.num = 4;
    stu3.name = "dsghhjrant";

    student stu4;
    stu4.num = 213;
    stu4.name = "fdsafgrw";

    add(stu2);
    add(stu3);
    add(stu4);

    printf("front\n");
    printList(front);
    printf("\ntop\n");
    printList(back);

    return EXIT_SUCCESS;
}

回答1:


OK i tried it is working. Only get by address student structs to your add function as below

add(&stu2);
add(&stu3);
add(&stu4);

and change function of add as below

int add(student *to_add) {

    container* tmp;
    tmp = createContainer(to_add);
    printf("added: (%d, %s)\n", tmp->data->num, tmp->data->name);

    back->next = tmp;
    back = tmp;

    return 1;
}



回答2:


Here's a more or less minimal version of your code with the add function. It runs at 60 lines compared to 83 for your original.

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

typedef struct student
{
    int num;
    char *name;
} student;

typedef struct container
{
    student *data;
    struct container *next;
} container;

static container *back;
static container *front;

static container *createContainer(student *data)
{
    container *tmp = malloc(sizeof(container));
    tmp->data = data;
    tmp->next = NULL;
    return tmp;
}

static void add(student to_add)
{
    container *tmp = createContainer(&to_add);
    printf("added: (%d, %s)\n", tmp->data->num, tmp->data->name);
    back->next = tmp;
    back = tmp;
}

static void printList(container *item)
{
    for (int i = 0; item != NULL; item = item->next)
        printf("%d:\t%d\t\t%s\n", ++i, item->data->num, item->data->name);
}

int main(void)
{
    student stu1 = { 6, "grefagf" };
    student stu2 = { 3, "dsghjyreawre" };
    student stu3 = { 4, "dsghhjrant" };
    student stu4 = { 213, "fdsafgrw" };

    front = createContainer(&stu1);
    back = front;
    add(stu2);
    add(stu3);
    add(stu4);

    printf("front\n");
    printList(front);
    printf("\nback\n");
    printList(back);

    return EXIT_SUCCESS;
}

On my Mac, it produced:

added: (3, dsghjyreawre)
added: (4, dsghhjrant)
added: (213, fdsafgrw)
front
1:  6       grefagf
2:  0       
3:  0       
4:  0       

back
1:  0   

The problem is that you are passing the address of a local variable to your createContainer() function, but that variable goes out of scope so your container is pointing at garbage. Since this is invoking undefined behaviour, the results can be different on your machine, and both will be correct. A crash is also possible — that's one of the beauties of UB.

You need to revise it in one of two ways. Either createContainer() makes a copy of what it is passed, or you arrange to pass pointers to the variables in main() through add() to createContainer(). This code does the second — but it is probably not the better solution in the long run. However, there's (a lot) more memory management to deal with for a general solution copying what's passed.

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

typedef struct student
{
    int num;
    char *name;
} student;

typedef struct container
{
    student *data;
    struct container *next;
} container;

static container *back;
static container *front;

static container *createContainer(student *data)
{
    container *tmp = malloc(sizeof(container));
    tmp->data = data;
    tmp->next = NULL;
    return tmp;
}

static void add(student *to_add)
{
    container *tmp = createContainer(to_add);
    printf("added: (%d, %s)\n", tmp->data->num, tmp->data->name);
    back->next = tmp;
    back = tmp;
}

static void printList(container *item)
{
    for (int i = 0; item != NULL; item = item->next)
        printf("%d:\t%d\t\t%s\n", ++i, item->data->num, item->data->name);
}

int main(void)
{
    student stu1 = { 6, "grefagf" };
    student stu2 = { 3, "dsghjyreawre" };
    student stu3 = { 4, "dsghhjrant" };
    student stu4 = { 213, "fdsafgrw" };

    front = createContainer(&stu1);
    back = front;
    add(&stu2);
    add(&stu3);
    add(&stu4);

    printf("front\n");
    printList(front);
    printf("\nback\n");
    printList(back);

    return EXIT_SUCCESS;
}

There are 5 characters different here from the last version. There's a * in the function definition of add(); there's no & in the call to createContainer(); there is an & in each of the calls to add() in main(). The result is:

added: (3, dsghjyreawre)
added: (4, dsghhjrant)
added: (213, fdsafgrw)
front
1:  6       grefagf
2:  3       dsghjyreawre
3:  4       dsghhjrant
4:  213     fdsafgrw

back
1:  213     fdsafgrw

This code leaks memory because it doesn't attempt to clean up the list. That's OK for the time being. Just be aware that you'll need to clean up eventually.




回答3:


The add function should also consume the linked list (or "container") that it is adding the "student" to.

Thus, you should not be creating a "temp" container in it, but using the passed in container and adding to the back of that passed in container.

EDIT: Just realized your back and front are global. This probably isn't a good idea, because that way you can only have a single linked list for your entire program.

Rereading now...

EDIT 2: Ahh this was a good one. You are passing in student (which makes a copy of the student) and then taking its address in the add function, which is an address of a variable on the stack. The stack is popped off so address to variables on the stack don't make sense. Instead, add should take in a pointer to a student, and you should pass in the address of stu2, stu3, etc.

EDIT 3: Ahh too late :(



来源:https://stackoverflow.com/questions/48735436/why-can-i-not-add-to-a-linked-list-correctly-when-using-a-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!