2016-11-01 5 views
0

Я пытаюсь просто перевернуть одноуровневый список, но с небольшим завихрением. Вместо того, чтобы указатель на следующий узел был фактическим следующим узлом, он указывает на указатель в следующем узле.Segfault при доступе к следующему узлу в одиночном списке

struct _Node 
{ 
    union 
    { 
     int n; 
     char c; 
    } val; 
    void *ptr; /* points to ptr variable in next node, not beginning */ 
    int var; 
}; 
typedef struct _Node Node; 

Я знаю, как обратный нормальный односвязан список, и я думаю, что у меня есть общее представление о том, как идти о решении этого, но я получаю Segfault, когда я пытаюсь получить доступ к head->ptr и Я не знаю почему.

Node *reverse(Node *head) 
{ 
    Node * temp; 
    Node * prev = NULL; 
    while(head != NULL) 
    { 
     temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */ 
     head->ptr = prev; 
     prev = head; 
     head = temp; 
    } 
    return prev; 
} 

Даже если я пытаюсь и доступ head->ptr без добавления 4, я получаю Segfault.

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

+0

ОК, чтобы уточнить: head-> prev указывает на «some_other_node.ptr»? –

+0

Если вы уточните, что указывает ptr, тогда это правильно. – John

+0

У вас есть веская причина для того, чтобы элемент 'ptr' указывал на элемент' ptr' следующего элемента в списке, вместо того, чтобы делать что-то обычным, разумным, простым, относительно надежным способом, который бы сделала любая нормальная программа , и имеет 'ptr' (вероятно, переименован как' next' и, вероятно, не из типа 'void *', поскольку тип добавляет еще один уровень сложности или не-переносимости), указывающий на начало следующего элемента в списке? Это кажется порочным. Единственное оправдание, которое я могу придумать, - «Садистский учитель, диктовал это». В противном случае это вряд ли будет разумным решением. –

ответ

1

Во-первых, я покажу вам главную проблему в коде:

while (head) // is shorter than while(head != NULL) 
    { 
     // Where does the 4 come from? 
     // And even if: You have to substract it. 
     // so, definitively a bug: 
     // temp = head->ptr + 4; /* add 4 to pass union and get beginning of next node */ 
     size_t offset_ptr = (char*)head->ptr - (char*)head; 
     // the line above should be moved out of the while loop. 
     temp = head->ptr - offset_ptr; 

В любом случае, ваш алгоритм, вероятно, не будет работать, как написано. Если вы хотите переделать материал, вам придется работать назад (что нетривиально в одиночных списках). Есть два варианта:

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

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

+0

Моя логика 4 заключалась в том, что размер объединения равен 4 байтам для учета размера переменной int. Вычитая это, можно было бы решить проблему смещения. – John

+0

да, но что, если вы получите второй объектный файл (со вторым заголовочным файлом), который не имеет объединения, а вместо него - структуру? Затем вы можете переписать весь свой код. Мое решение все равно будет работать. Вот почему мы обычно так делаем. Никогда не записывайте константы (например, «4»), если выполняются задания постоянных выражений (например, «(char *) head-> ptr - (char *) head»). –

+0

Определенно правильный способ взглянуть на него, вы правы. Теперь я остаюсь с бесконечным циклом, но я думаю, это лучше, чем segfault! – John

1

Я пробовал ваш код и делал некоторые настройки, ну, на мой взгляд, у вашего кода была некоторая логическая ошибка. Ваши указатели были перезаписаны снова и снова (прыгая с одного узла на другой и обратно: 1-> 2, 2-> 1), которые приводили к подозрению в утечке памяти. Здесь рабочая версия вашего кода ...

Node *reverse(Node *head) 
{ 
    Node *temp = 0; 
    //Re-ordering of your assignment statements 
    while (head) //No need for explicit head != NULL 
    { 
     //Here this line ensures that pointers are not overwritten 
     Node *next = (Node *)head->ptr; //Type casting from void * to Node * 
     head->ptr = temp; 
     temp = head; 
     head = next; 
    } 
    return temp; 
}