2013-06-18 45 views
1

我基於相當數量的代碼的雙向鏈表似乎有一個bug,它與我從列表中刪除節點的方式有關,但我無法察覺它。請看下面的代碼:正確使用函數指針

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

代碼對我來說也很好看。假設所有的指針(next,previous和object)都是有效的或NULL,應該沒問題。 – Zenilogix

+1

最微妙的部分似乎是memcpy。你確定'qu_remove'的第二個參數是指向一個足夠大的內存區域嗎? –

回答

1

我發現了這個問題。它不在代碼中,但是我的理解爲。爲了說明這一點,考慮從功能qu_remove以下行:

memcpy(object, marker, &size); 

此前呼籲memcpyobject的內容和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! :p – 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