Копирование элемента указателя структуры в указатель, кажется, повреждает его - PullRequest
5 голосов
/ 30 мая 2011

Я все еще изучаю C, и в какой-то момент у меня должны быть неправильно понятые указатели.

У меня сложилось впечатление, что следующие строки будут копировать адрес памяти, хранящийся в l->first в temp. Они оба struct list_el* указатели, поэтому я не вижу проблемы.

struct list_elt * temp;
temp = l->first;

Выполнение моего примера кода дает мне бесконечный цикл:

user@machine:~$ gcc question.c 
user@machine:~$ ./a.out | head
append(): l->first->val: 30, l->first->next == NULL: 1
main()  : l->first->val: 30, l->first->next == NULL: 1
print() : l->first->val: 30, l->first->next == NULL: 1
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0
print() : temp->val: 30, temp->next == NULL: 0

Вот question.c. Мне жаль, что я не мог сузить это дальше, каждый раз, когда я делаю, проблема, кажется, волшебным образом исчезает.

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

struct list_elt {
    int val;
    struct list_elt * next;
};
struct linked_list {
    struct list_elt* first;
};

void print(const struct linked_list * l);
struct linked_list* new_list(void);
void append(struct linked_list* l, int value);


main()
{
    struct linked_list * l;
    l = new_list();
    append(l, 30);
    printf("main()  : l->first->val: %d, l->first->next == NULL: %d\n", l->first->val, l->first->next == NULL);
    print(l);
}

struct linked_list* new_list()
{
    struct linked_list* l;
    l = (struct linked_list*) malloc(sizeof(struct linked_list));
    l->first = NULL;
    return l;
}

void append(struct linked_list* l, int value)
{
    struct list_elt el = {0, NULL};
    el.val = value;
    el.next = NULL;
    if (l->first == NULL) {
        l->first = (struct list_elt*) &el;
        printf("append(): l->first->val: %d, l->first->next == NULL: %d\n", l->first->val, l->first->next == NULL);
    } else {
        printf("append(): Unimplemented\n");
    }
}


void print(const struct linked_list * l)
{
    printf("print() : l->first->val: %d, l->first->next == NULL: %d\n", l->first->val, l->first->next == NULL);
    struct list_elt * temp;
    temp = l->first;
    while (temp != NULL) {
        printf("print() : temp->val: %d, temp->next == NULL: %d\n", temp->val, temp->next == NULL);
        temp = temp->next;
    }
    printf("\n");
}

Спасибо.

Ответы [ 5 ]

5 голосов
/ 30 мая 2011

Вы размещаете свой list_elt в стеке функции append. этот указатель недействителен после возврата из функции, и на него больше нельзя ссылаться.

Вы должны выделить ваши элементы с malloc.

3 голосов
/ 30 мая 2011

(Ниже приводится очень простое объяснение, настолько простое, что оно, возможно, неточно. Я пошел за простотой, но вы можете узнать больше, погуглив о стеке и куче в C).

Существует два основных способа создания вещей и их сохранения в памяти: использование стека или запрос памяти в операционной системе .

Стек - это временная память , используемая для отслеживания текущего контекста . Каждый раз, когда вы вызываете функцию, сектор стека используется для хранения всех локальных переменных. Это то, что вы делаете в append(): , используя временную память, расположенную в стеке, для хранения вашего нового узла списка .

Все, что находится в стеке (любые локальные переменные) считается потерянным во времени и пространстве, когда вы возвращаетесь из функции , поскольку текущий контекст выполнения больше не актуален, и это память используется повторно для другого контекста (например, при следующем вызове функции). Это неизбежно, это следствие того, как управляется эта память, и это происходит за пределами вашего прямого доступа в C (или, скорее, вы не должны связываться с этим).

Если вы хотите сохранить что-то вне этой локальной области, относящееся к вашей программе в целом, а не только к текущему контексту выполнения, вы должны запросить дополнительную память для операционной системы - это временно стек не подойдет. Для этого существует множество функций - посмотрите простейшие, называемые malloc().

Надеюсь, это помогло. Ура!

3 голосов
/ 30 мая 2011

В append() вы устанавливаете l->first на адрес локальной стековой переменной el. Но el выходит из области видимости, как только возвращается append(), поэтому любая попытка разыменования l->first впоследствии будет неопределенным поведением .

1 голос
/ 30 мая 2011

Ваша append функция неверна.Он размещает el в стеке и использует указатель в вашем списке.Проблема в том, что память будет перезаписана мусором, как только другая функция будет вызвана после append, в данном случае print.Вместо этого выделите с помощью malloc что-то вроде:

int append(struct linked_list* l, int value)
{
    struct list_elt *el = malloc(sizeof(struct list_elt));

    if (el)
    {
        el->val = value;
        el->next = NULL;
        if (l->first == NULL) {
            l->first = el;
            printf("append(): l->first->val: %d, l->first->next == NULL: %d\n",
                   l->first->val, l->first->next == NULL);
        } else {
            printf("append(): Unimplemented\n");
        }

        return 0;
    }
    else
    {
        return 1;
    }
}
0 голосов
/ 30 мая 2011

При добавлении ваш элемент создается в стеке и на него ссылаются позже: это неправильно.Когда ваша функция выходит из вашей переменной el больше не существует.Вам нужно переместить ваш список_перечисления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...