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ますが、 function に渡された object パラメーターを破棄しているように見えます。これは、パラメーターのオブジェクトではなく、破棄さqu_removeれるオブジェクトのオブジェクトである必要があるため、私には非常に奇妙に思えfirst_nodeます。

4

2 に答える 2

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;
}
于 2013-06-18T22:57:52.027 に答える
1

問題が見つかりました。それはコードにあるのではなく、私の理解にあるのです。説明のために、関数の次の行を考えてみましょうqu_remove

memcpy(object, marker, &size);

を呼び出す前の との内容memcpyは次のとおりです。objectmarkerqu_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 ( の識別子の場所marker) から 0x1FFF8668 ( の識別子の場所) にコピーすると思いましたobject。これはナンセンスであることがわかりました。文字「N」は の一部ではないmarkerため、コピーされません。「N」へのポインタのみがコピーされます。

これを知ることで、bu_test関数の失敗が説明されます。トリッキーな部分は、問題を解決する方法を見つけ出すことです。私が必要とするのはmemcpy、ポインタチェーンを指しているオブジェクトまでたどり、それもコピーするための代替を書くことです。

于 2013-06-20T00:22:53.850 に答える