Курсор в связанном списке меняет значение без видимой причины - PullRequest
0 голосов
/ 31 марта 2012

Редактировать

Для простоты: Я просто хочу сделать самый простой возможный курсор, который просто идетчерез мой список без каких-либо изменений / формы или формы моего «начала».Я все еще хочу начать меняться, когда я возвращаю что-то, хотя, только не раньше.Так можно ли сделать указатель для начала, пройти по списку, не меняя ничего, кроме как в самом конце, когда я хочу добавить новый узел?

Также Могу ли я просто пройти по списку с помощью простого указателя, который не является «Узлом»?

/ Edit

У меня есть простой(отдельно) связанный список, чтобы сделать как часть моей домашней работы.Конечно, у меня также есть много дел, кроме этого, но после того, как я получу список, все должно быть прямо вперед, но пока я некоторое время пользуюсь C ++ (это был Borland C ++), многое из того, что язнал или наполовину забыт или устарел.Некоторое время я программировал на python, но все это означает, что я продолжаю расстраиваться из-за того, как работают указатели в C ++.

Моя проблема в том, что когда я пытаюсь добавить новый узел в список, мой курсор ведет себя внеобычным образом, я объясню ниже:

РЕДАКТИРОВАТЬ: Хорошо, я изменил:

Node *cursor;
cursor = new Node
cursor = begin;

фиаско, но результатто же самое, после объявления курсора и начала обе точки указывают на одну и ту же ячейку памяти (что-то вроде: 0x32ce8).

/ EDIT

Node *add_node (Node *begin,string type, int sum, int ap_nr) // begin is the first node in the list
{
    // if first node is dummy node

    if (begin->ap_nr == -1)
        {
            begin->type = type;
            begin->ap_nr = ap_nr;
            begin->sum = sum;
            begin->next = 0;
            return begin;
        }

    // else create new node and insert it in sorted position

   else
    {
        // EDIT:
        Node *cursor = begin; // Same problem

        //if node should be inserted before first node (begin)

        if (ap_nr <begin->ap_nr)
        {
            cursor->ap_nr = ap_nr;
            cursor->type = type;
            cursor->sum = sum;
            cursor->next = begin;
            return cursor;
        }

Всегда, когда я отлаживаю, начало имеет похожую форму: 0x32ce02, когда я создаю свой «курсор», он имеет совершенно другую форму (также длиннее), но когда я делаю это: курсор = начало, тогда курсор становится чем-то вроде этого 0x32df02.

Однако проблема в том, что когда я добираюсь до «if (ap_nr ap_nr)», тогда по абсолютно невыполнимой причине курсор становится: 0x32ce02, а «cursor -> next = begin» обеспечивает бесконечный цикл.И независимо от того, сколько узлов я добавляю, это всегда происходит, поэтому всякий раз, когда я печатаю список, это бесконечный поток последнего добавленного узла.

Я делаю что-то не так?это декларация или размещение, создание?что-то?

Также, если у меня есть указатель * начать где-то в другом модуле, и с помощью этой функции я возвращаю новое начало ..., которое должно работать, верно?

PS Я также был бы признателенпростое встречное решение (еще один способ сделать это, если мой просто не годится)

Также я должен указать, как я составил свой список.Это просто простая связь узлов:

struct Node {
    string type;
    int ap_nr;
    int sum;
    Node *next;
};

Ответы [ 3 ]

2 голосов
/ 31 марта 2012

Этот код:

cursor = new Node;
cursor = begin;

Выполняет следующие действия:

  • Создать новый объект Node в стеке
  • Поместить адрес этого объекта в переменнуюcurrent
  • Немедленно перезаписать этот адрес адресом, который begin содержит

, т. Е. Все, что он делает - это утечка нового Node.cursor и begin после этого указывают на одно и то же.

Таким образом, строка:

cursor->next = begin;

делает цикл.cursor->next == begin == cursor.

Удаление строки cursor = begin; и возврат курсора сделают то, что вы хотите.Узел, созданный с помощью new Node, будет новым заголовком списка, а тот, на который begin указал в начале функции, будет вторым узлом в этом списке.

Теперь, когда вы хотитечтобы пройти (не добавить) в этот список, вы можете сделать что-то вроде:

 Node *cursor = begin; // assuming begin is the head of your list
 while (cursor != 0) {
    // process this node
    cursor = cursor->next;
 }

И это будет посещать каждый узел в вашем списке, если вы правильно его построили.

Aнесколько замечаний:

  • Код, который вызывает эту функцию, вероятно, должен выглядеть примерно так:

    list = add_node(list, ...);
    

    Если у вас его нет, вы не получите то, чтовы хотите.

  • Если адрес, который вы видите в begin, выглядит очень отличным от адреса, возвращенного new, вы можете сделать что-то не такто есть begin может указывать на адрес стека.Не помещайте объекты из вашего стека в список, убедитесь, что вы все выделяете их с помощью new.(Если begin указывает на глобальную статическую переменную, это нормально - но убедитесь, что вы не delete это).

  • Если вы на самом деле не нуждаетесь фиктивный узел, я бы посоветовал вам удалить это тоже - это делает вашу add_node функцию более сложной, чем она должна быть.

Вот скелет того, как вы это сделаете безэтот специальный фиктивный узел (неполный код):

Node *add_node(Node *list, ...)
{
  Node *new_node = new Node;
  new_node->next = list;
  // fill in other properties
  return new_node;
}

И использовать это:

int foo()
{
    Node *list = 0; // or nullptr for C++11
    list = add_node(list, ...); // add item 1
    list = add_node(list, ...); // add item 2
    list = add_node(list, ...); // add item 3
    ...
1 голос
/ 31 марта 2012

Указатели cursor и begin указывают на одну и ту же область памяти, потому что вы явно говорите так: Node* cursor = begin; буквально говорит "создать переменную-указатель с именем cursor, которая указывает на то же место как begin. " Поэтому неудивительно, что это так.

Редактировать: Удален совет, основанный на неверной догадке, для чего предназначен код, и он заменен на более применимый совет

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

Для этого случая инициализация cursor теперь, конечно, правильная. Однако не правильно изменить объект cursor указывает на: Вы хотите вставить новый узел перед этого узла. Но для этого нужно внести несколько изменений:

Во-первых, вам нужна другая переменная-указатель , которая содержит указатель на вновь созданный узел, например

Node* new_node = new Node;

И затем вы должны вставить этот узел в список. То есть вместо cursor->ap_nr=ap_nr; и т. Д. Вы должны использовать new_node->ap_nr=ap_nr; и т. Д. Кроме того, следующий за ним узел, конечно, не первый узел списка (на который указывает first), но тот, который вы только что нашли (на что указывает current).

Однако теперь у вас есть проблема: вам нужно вставить этот новый узел в список, что означает, что вы должны изменить указатель next предыдущего узла (но указывать не на begin, но только что созданному узлу!). Но у вас больше нет указателя на предыдущий узел, потому что ваш список односвязный, то есть у вас нет указателя от найденного элемента на предыдущий элемент. Но чтобы вставить элемент, у вас есть , чтобы изменить next.

Однако у do есть указатель на следующий узел . Следовательно, лучшая стратегия состоит в том, чтобы ваш cursor указывал на предыдущий узел , а затем последовательно использовал cursor->next вместо cursor (за исключением, конечно, перемещения cursor). Таким образом, вы можете после установить new_node->next, написать cursor->next = new_node;

Другими вещами, которые отсутствуют в вашем коде, являются проверки того, что current не является нулевым (что будет в конце списка), и что код фактически перемещает ваш cursor вперед (который принадлежит к else часть вашего внутреннего if).

На самом деле, теперь я замечаю, что ваши блоки не закрыты, поэтому в вашем текущем коде может присутствовать код продвижения вперед.

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

1 голос
/ 31 марта 2012
    Node *cursor;
    cursor = new Node;
    cursor = begin;

Точно поймите, что вы делаете с этими тремя строками здесь - может происходить больше, чем вы на самом деле понимаете.

  • В первой строке вы объявляете новую переменную с именем cursor - эта переменная является именем для объекта указателя, который имеет тип Node *.(На простом английском языке Node * означает «указатель на Node »).
    Тип данных, хранящихся в объекте указателя, представляет собой просто числовые целочисленные данные, которые представляют адрес в памяти.объект-указатель на самом деле ничем не отличается от любого другого целочисленного объекта.
    (Обратите внимание на разницу между «переменной» и «объектом» - объект - это нечто, хранящееся в памяти, например число / символ / адрес памятии переменная представляет собой имя для объекта)

  • Во второй строке происходят две вещи - во-первых, вы выделяете память для Узел Объект и построить новый Узел объект в этой выделенной памяти - эта память не имеет имени переменной, это просто объект свободного хранилища (Это краткая версия того, как новый узел работает), затем вы используете оператор = (оператор присваивания) для сохранения значения адреса (числа) вновь выделенного узла объекта в переменной cursor .
    После сохранения значения адреса нового объекта Node единственный способ получить доступ к вновь выделенному объекту Node - через имя переменной курсора.

  • В третьей строке вынемедленныйly перезаписать значение, сохраненное вашей переменной курсора;вместо сохранения данных адреса для нового узла он сохраняет адрес другого объекта (адрес этого другого объекта скопирован из другой переменной-указателя с именем begin ).Это немедленно приводит к тому, что вновь выделенный Узел будет "Утечка".

Точка, которую я пытаюсь донести (что, я полагаю, вы могли быне осознавать), что в этих трех строках кода есть потенциально четыре отдельных объекта (то есть «элементы в памяти»).Два из них являются объектами-указателями (целыми числами), называемыми cursor и begin , другие два являются объектами бесплатного хранения (узлами), которые не имеют имени, но доступны с использованием сохраненных значений адресовпо вашим указателям объектов.

Существует несколько ссылок, которые вы можете прочитать здесь:

...