2013-06-18 4 views
1

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

typedef struct DL_LIST 
{ 
    uint16 tag; 
    struct DL_LIST *previous; 
    struct DL_LIST *next; 
    void *object; 
    uint32 size; 
} DL_LIST; 

Функция используется для удаления узлов следующим образом:

void dl_delete(DL_LIST *node, void (*destructor)(void*)) 
{ 
    assert(destructor != NULL); 

    if (node != NULL) 
    { 
     dl_extract(node); 

     if (node->object != NULL) 
     { 
      destructor(node->object); 
     } 

     free(node); 
    } 
} 

где:

DL_LIST *dl_extract(DL_LIST *node) 
{ 
    if (node != NULL) 
    { 
     if (node->previous != NULL) 
     { 
      node->previous->next = node->next; 
     } 

     if (node->next != NULL) 
     { 
      node->next->previous = node->previous; 
     } 

     node->previous = NULL; 
     node->next = NULL; 
    } 

    return node; 
} 

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

EDIT 1. В соответствии с просьбой в комментариях, код очереди следующим образом:

typedef struct QU_LIST 
{ 
    DL_LIST *list; 
    uint32 count; 
} QU_LIST; 

uint8 qu_remove(QU_LIST *queue, void *object, void (*destructor)(void*)) 
{ 
    uint8 result = QU_SUCCESS; 
    uint32 size; 
    DL_LIST *first_node; 
    DL_LIST *next_node; 
    void *marker; 

    assert(queue != NULL && destructor != NULL); 

    if (queue->count > 0) 
    { 
     first_node = dl_get_first(queue->list); 
     next_node = dl_get_next(first_node); 

     marker = dl_get_object(first_node, NULL, &size); 

     if (marker != NULL) 
     { 
      if (object != NULL) 
      { 
       memcpy(object, marker, size); 
      } 
     } 
     else 
     { 
      result = QU_NO_MEMORY; 
     } 

     queue->list = next_node; 

     dl_delete(first_node, destructor); // this is the problem 

     --queue->count; 
    } 
    else 
    { 
     result = QU_EMPTY; 
    } 

    return result; 
} 

где:

DL_LIST *dl_get_first(DL_LIST *list) 
{ 
    if (list != NULL) 
    { 
     while (list->previous != NULL) 
     { 
      list = list->previous; 
     } 
    } 

    return list; 
} 

DL_LIST *dl_get_next(DL_LIST *node) 
{ 
    if (node != NULL) 
    { 
     node = node->next; 
    } 

    return node; 
} 

void *dl_get_object(DL_LIST *node, uint16 *tag, uint32 *size) 
{ 
    void *marker = NULL; 

    if (node != NULL) 
    { 
     if (tag != NULL) 
     { 
      *tag = node->tag; 
     } 

     if (size != NULL) 
     { 
      *size = node->size; 
     } 

     marker = node->object; 
    } 

    return marker; 
} 

EDIT 2. Благодаря стерлингов ответ со стороны Wumpus Q. Wumbley , источник проблемы был сужен до следующего кода, который является частью библиотеки кнопок навигации для встроенной системы.

void bu_test(void) 
{ 
    QU_LIST button_list = {0}; 
    BU_OBJECT *object = NULL; 

    object = bu_create("O"); 
    // object->identifier is "O" at this point. 

    bu_add(&button_list, "N"); 
    bu_add(&button_list, "S"); 
    bu_add(&button_list, "E"); 
    bu_add(&button_list, "W"); 

    qu_remove(&button_list, object, (void(*)(void*)) &_destructor); 
    // object->identifier should be "N" at this point, but is not. 
} 

где:

typedef struct BU_OBJECT 
{ 
    char *identifier; 
} BU_OBJECT; 

uint8 bu_add(QU_LIST *queue, char *identifier) 
{ 
    uint8 result = BU_SUCCESS; 
    BU_OBJECT* object; 

    assert(queue != NULL && identifier != NULL); 

    object = bu_create(identifier); 

    if (object != NULL) 
    { 
     result = qu_add(queue, _TAG, object, sizeof(*object)); 

     if (result == QU_NO_MEMORY) 
     { 
      _destructor(object); 

      result = BU_NO_MEMORY; 
     } 
    } 
    else 
    { 
     result = BU_NO_MEMORY; 
    } 

    return result; 
} 

и:

BU_OBJECT *bu_create(char *identifier) 
{ 
    BU_OBJECT *object = NULL; 
    char *p; 

    assert(identifier != NULL); 

    object = malloc(sizeof(*object)); 

    if (object != NULL) 
    { 
     p = malloc(sizeof(*identifier)); 

     if (p != NULL) 
     { 
      strcpy(p, identifier); 
      object->identifier = p; 
     } 
     else 
     { 
      free(object); 
      object = NULL; 
     } 
    } 

    return object; 
} 

и, наконец:

void _destructor(BU_OBJECT *object) 
{ 
    free(object->identifier); 
    free(object); 
} 

Объекты добавляются к button_list без ошибок, но кажется, что _destructor уничтожает параметр объекта, который передается функции qu_remove, что кажется мне чрезвычайно странным, так как это должен быть объект уничтоженного first_node, а не параметра.

+1

Я читал, что не заметил никаких ошибок. Опубликовать еще –

+0

Код выглядит хорошо для меня тоже. Предполагая, что все указатели (следующий, предыдущий и объект) являются действительными или NULL, должны быть в порядке. – Zenilogix

+1

Самая деликатная часть кажется memcpy. Вы уверены, что второй аргумент 'qu_remove' указывает на достаточно большую область памяти? –

ответ

1

Я нашел проблему. Он лежит не с кодом, а с моим пониманием.Чтобы проиллюстрировать это, рассмотрим следующую строку из функции qu_remove:

memcpy(object, marker, &size); 

До вызова memcpy, содержание object и в qu_remove заключаются в следующем:

Location  Content  Location Description 
--------  -------  -------------------- 
0x1FFF8658 0x1FFF8668 Pointer to object (object) 
0x1FFF8668 "O"   Pointer to identifier 

0x1FFF86B0 0x1FFF8688 Pointer to object (marker) 
0x1FFF8688 "N"   Pointer to identifier 

После вызова memcpy , содержание object выглядит следующим образом:

Location  Content  Location Description 
--------  -------  -------------------- 
0x1FFF8658 0x1FFF8688 Pointer to object (marker) 
0x1FFF8688 "N"   Pointer to identifier 

По какой-то причине я думал, что memcpy будет копировать символ «N» из местоположения 0x1FFF8688 (расположение идентификатора в) в 0x1FFF8668 (расположение идентификатора в object). Теперь я вижу, что это вздор. Символ «N» не является частью и поэтому не копируется - копируется только указатель на «N».

Знание этого объясняет отказ функции bu_test. Трудный бит будет определять способ решения проблемы. Мне понадобится написать замену для memcpy, которая следует за целыми цепочками указателей вплоть до объекта, на который указана точка, и копирует его.

+0

+1 для публикации подробного объяснения проблемы для других. Не будьте denvercoder! :п – Thomas

3

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

#include <stdio.h> 
#include <string.h> 
#include <stdint.h> 
#include <stdlib.h> 
#include <assert.h> 

typedef uint8_t uint8; 
typedef uint16_t uint16; 
typedef uint32_t uint32; 
enum { QU_SUCCESS, QU_NO_MEMORY, QU_EMPTY }; 

typedef struct DL_LIST 
{ 
    uint16 tag; 
    struct DL_LIST *previous; 
    struct DL_LIST *next; 
    void *object; 
    uint32 size; 
} DL_LIST; 

DL_LIST *dl_extract(DL_LIST *node) 
{ 
    if (node != NULL) 
    { 
     if (node->previous != NULL) 
     { 
      node->previous->next = node->next; 
     } 

     if (node->next != NULL) 
     { 
      node->next->previous = node->previous; 
     } 

     node->previous = NULL; 
     node->next = NULL; 
    } 

    return node; 
} 

void dl_delete(DL_LIST *node, void (*destructor)(void*)) 
{ 
    assert(destructor != NULL); 

    if (node != NULL) 
    { 
     dl_extract(node); 

     if (node->object != NULL) 
     { 
      destructor(node->object); 
     } 

     free(node); 
    } 
} 

DL_LIST *dl_get_first(DL_LIST *list) 
{ 
    if (list != NULL) 
    { 
     while (list->previous != NULL) 
     { 
      list = list->previous; 
     } 
    } 

    return list; 
} 

DL_LIST *dl_get_next(DL_LIST *node) 
{ 
    if (node != NULL) 
    { 
     node = node->next; 
    } 

    return node; 
} 

void *dl_get_object(DL_LIST *node, uint16 *tag, uint32 *size) 
{ 
    void *marker = NULL; 

    if (node != NULL) 
    { 
     if (tag != NULL) 
     { 
      *tag = node->tag; 
     } 

     if (size != NULL) 
     { 
      *size = node->size; 
     } 

     marker = node->object; 
    } 

    return marker; 
} 

typedef struct QU_LIST 
{ 
    DL_LIST *list; 
    uint32 count; 
} QU_LIST; 

uint8 qu_remove(QU_LIST *queue, void *object, void (*destructor)(void*)) 
{ 
    uint8 result = QU_SUCCESS; 
    uint32 size; 
    DL_LIST *first_node; 
    DL_LIST *next_node; 
    void *marker; 

    assert(queue != NULL && destructor != NULL); 

    if (queue->count > 0) 
    { 
     first_node = dl_get_first(queue->list); 
     next_node = dl_get_next(first_node); 

     marker = dl_get_object(first_node, NULL, &size); 

     if (marker != NULL) 
     { 
      if (object != NULL) 
      { 
       memcpy(object, marker, size); 
      } 
     } 
     else 
     { 
      result = QU_NO_MEMORY; 
     } 

     queue->list = next_node; 

     dl_delete(first_node, destructor); // this is the problem 

     --queue->count; 
    } 
    else 
    { 
     result = QU_EMPTY; 
    } 

    return result; 
} 

DL_LIST *dl_get_last(DL_LIST *list) 
{ 
    if (list != NULL) 
    { 
     while (list->next != NULL) 
     { 
      list = list->next; 
     } 
    } 

    return list; 
} 

DL_LIST **qu_get_tail(QU_LIST *queue) 
{ 
    DL_LIST *node = dl_get_last(queue->list); 
    if(node) 
     return &node->next; 
    return &queue->list; 
} 

uint8 qu_add(QU_LIST *queue, uint16 tag, void *object, uint32 size) 
{ 
    DL_LIST *node = malloc(sizeof *node), *prev; 
    if(!node) 
    return QU_NO_MEMORY; 
    node->next = NULL; 
    node->tag = tag; 
    node->object = object; 
    node->size = size; 
    if(queue->list) { 
     prev = dl_get_last(queue->list); 
     prev->next = node; 
     node->previous = prev; 
    } else { 
     queue->list = node; 
     node->previous = NULL; 
    } 
    ++queue->count; 
    return QU_SUCCESS; 
} 

void qu_init(QU_LIST *queue) 
{ 
    queue->list = NULL; 
    queue->count = 0; 
} 

void destroydata(void *data) 
{ 
    memset(data, 'X', 3); 
} 

int main(void) 
{ 
    char testdata[] = "ABC DEF GHI JKL!"; 
    char removed[4] = ""; 
    int i; 
    QU_LIST q; 

    qu_init(&q); 
    if(qu_add(&q, 0, &testdata[0], 3) != QU_SUCCESS) abort(); 
    if(qu_add(&q, 1, &testdata[4], 3) != QU_SUCCESS) abort(); 
    if(qu_add(&q, 2, &testdata[8], 3) != QU_SUCCESS) abort(); 
    if(qu_add(&q, 3, &testdata[12], 3) != QU_SUCCESS) abort(); 
    puts("Done adding"); 
    for(i=0;i<4;++i) { 
     if(qu_remove(&q, removed, destroydata) !=QU_SUCCESS) abort(); 
     printf("Removed: %s\n", removed); 
     printf("testdata now contains: %s\n", testdata); 
    } 
    return 0; 
} 
+0

Превосходный ответ - спасибо, что нашли время, чтобы играть. В настоящее время я кричу на свой компьютер, пытаясь найти источник проблемы в коде, который не отображается. Если все остальное не удастся, я добавлю его в конец моего вопроса. Еще раз спасибо. – RBE