【问题标题】:Crash with circular linked list循环链表崩溃
【发布时间】:2016-12-07 23:19:42
【问题描述】:

我正在尝试实现一个链表(我相信这是一个循环链表),但在使用它时,它最终会崩溃。我认为这是因为我使用的遍历指针未正确设置为 0,最终被取消引用并表现出未定义的行为。

感谢您的帮助。

这是我正在使用的功能:

struct records t_head = 0;

struct records* find_id(Rect a)
{

    struct records* tmp;
    struct records* prev;

    // Add first node
    if (t_head == NULL) {
        tmp = new records;
        tmp->b = a;
        tmp->id = trecord_count + 1;
        tmp->tally.resize(labelsInfo.size());
        tmp->frames = 0;
        t_head = tmp;
        t_head->next = NULL;
        trecord_count++;
    }

    else {
        // Check if there is any node that has delete_flag set first
        tmp = t_head;
        while (tmp) {

            if (delete_flag) {

                // Delete here
                if (tmp == t_head) {
                    t_head = tmp->next;
                    delete (tmp);
                    trecord_count--;
                }
                else {
                    prev->next = tmp->next;
                    delete (tmp);
                    trecord_count--;
                }
            }

            prev = tmp;
            tmp = tmp->next;
        }

        tmp = new records;
        tmp->b = a;
        tmp->id = trecord_count + 1;
        tmp->tally.resize(labelsInfo.size());
        tmp->frames = 0;
        tmp->next = t_head;
        t_head = tmp;
        trecord_count++;
    }

    return tmp;
}

结构定义为:

struct records {
int id;
Rect b;
vector<int> tally= {};
int frames=0;
int delete_flag=0;
struct records *next;
};

【问题讨论】:

  • 你能发布更完整的代码吗?这不是凭空出现的吗?
  • 这是标记为 C,但您使用的是 newdelete;你确定你没有使用 C++ 编译器吗?
  • 您需要向我们提供复制问题所需的最少代码。这意味着我们应该能够编译和运行它,从而复制问题。但是您应该尽可能多地删除代码。 (在准备这个的过程中,你可能会发现自己解决了这个问题。)
  • 除非这是一个学习更多关于编码的学术练习,否则这完全是浪费时间。 C++ 标准库有各种各样的容器,包括像链表一样操作的容器。为什么在 C++ 中使用struct?这应该是class。它应该有一个初始化器来正确填充next 之类的东西。它应该有测试。
  • 是什么阻止了prev 指向在tmp != t_head 时被删除的内存?查看代码,这似乎是可能的,因为您 delete tmp 然后将您刚刚删除的内容分配给 prev

标签: c++


【解决方案1】:

以下代码运行,但除了 cmets 中已经提到的这些之外,它还显示了缺陷。

  1. 在您的版本中,您可以访问tmp 中的指针,您刚刚在循环中删除了该指针,以使用集合delete_flag 搜索记录。这在下面的版本中得到纠正。所需的修改标有//&lt; Tobias。请注意,在 linux 下使用 valgrind 很容易发现此类错误。 Andon M. Coleman 也提到了这个in his comment
  2. 在您的find_id 版本中,实际上没有实现对a 的搜索。我已经在我的版本中实现了这样的搜索。
  3. 如果您删除列表中间的某些记录,则结束记录会保留其 ID,但您会减少 trecord_count 并再次分配相同的 ID。因此,您最终会得到多个具有相同 ID 的记录。

编辑:我在程序中添加了一些日志记录。也许,这可以帮助您了解正在发生的事情。此外,我将frames 误用作真正的id。即,没有两条记录具有相同的frames-value。记录在大括号中,完整列表在方括号中。 NO 你没有循环列表。可能是因为在前面添加了新记录,所以你会产生错误的印象,因此,你需要使用 t_head 的旧值作为新创建记录的 next 指针的值。

程序的输出是:

g++ -std=c++11 -g -O0 ./test.cc -o a.exe && ./a.exe
Returning { frames=1 id=1 b=1 }
Full content:
[ { frames=1 id=1 b=1 } ]

Adding { frames=2 id=2 b=2 }
Returning { frames=2 id=2 b=2 }
Full content:
[ { frames=2 id=2 b=2 }, { frames=1 id=1 b=1 } ]

Adding { frames=3 id=3 b=3 }
Returning { frames=3 id=3 b=3 }
Full content:
[ { frames=3 id=3 b=3 }, { frames=2 id=2 b=2 }, { frames=1 id=1 b=1 } ]

Deleting head { frames=3 id=3 b=3 }
Adding { frames=4 id=3 b=4 }
Returning { frames=4 id=3 b=4 }
Full content:
[ { frames=4 id=3 b=4 }, { frames=2 id=2 b=2 }, { frames=1 id=1 b=1 } ]

Returning { frames=2 id=2 b=2 }
Full content:
[ { frames=4 id=3 b=4 }, { frames=2 id=2 b=2 }, { frames=1 id=1 b=1 } ]

Deleting { frames=2 id=2 b=2 }
Adding { frames=5 id=3 b=5 }
Returning { frames=5 id=3 b=5 }
Full content:
[ { frames=5 id=3 b=5 }, { frames=4 id=3 b=4 }, { frames=1 id=1 b=1 } ]


At exit: [ { frames=5 id=3 b=5 }, { frames=4 id=3 b=4 }, { frames=1 id=1 b=1 } ]

下面是名为test.cc的程序:

#include <vector>
#include <iostream>
#include <cstdlib>

typedef int Rect;

struct records {
    int id;
    Rect b;
    std::vector<int> tally= {};
    int frames=0;
    int delete_flag=0;
    struct records *next;
};

struct records *t_head = 0;
int trecord_count = 0;
int trecord_frames = 0;

std::ostream& operator << (std::ostream& os, const records& r) {
    os << "{ frames=" << r.frames << " id=" << r.id << " b=" << r.b << " }";
    return os;
}

std::ostream& operator << (std::ostream& os, const records* r) {
    os << "[ ";
    if(r)
        os << *r;
    for(r=r->next; r; r=r->next)
        std::cout << ", " << *r;
    std::cout << " ]\n";
}

struct records* find_id(Rect a)
{

    struct records* tmp;
    struct records* prev;

    // Add first node
    if (t_head == NULL) {
        tmp = new records;
        tmp->b = a;
        tmp->id = trecord_count + 1;
        tmp->tally.resize(0);
        tmp->frames = ++trecord_frames;
        t_head = tmp;
        t_head->next = NULL;
        trecord_count++;
    }

    else {
        // Check if there is any node that has delete_flag set first
        tmp = t_head;
        while (tmp) {

            if (tmp->delete_flag) {

                // Delete here
                if (tmp == t_head) {
                    std::cout << "Deleting head " << *tmp << std::endl;
                    t_head = tmp->next;
                    delete (tmp);
                    trecord_count--;
                    tmp = t_head; //< Tobias
                }
                else {
                    std::cout << "Deleting " << *tmp << std::endl;
                    prev->next = tmp->next;
                    delete (tmp);
                    trecord_count--;
                    tmp = prev->next; //< Tobias
                }
            } else {
                prev = tmp;
                tmp = tmp->next;
            }
        }

        // Search for the id
        tmp = t_head;
        while(tmp && (tmp->b != a))
            tmp = tmp->next;

        if(!tmp) {
            tmp = new records;
            tmp->b = a;
            tmp->id = trecord_count + 1;
            tmp->tally.resize(0);
            tmp->frames = ++trecord_frames;
            tmp->next = t_head;
            t_head = tmp;
            std::cout << "Adding " << *tmp << std::endl;
            trecord_count++;
        }
    }

    std::cout << "Returning " << *tmp << std::endl;
    std::cout << "Full content:\n" << t_head << std::endl;

    return tmp;
}

int main() {
    find_id(1);
    find_id(2);
    find_id(3);

    t_head->delete_flag=1;
    find_id(4);

    records* r = find_id(2);
    r->delete_flag = 1;
    find_id(5);

    std::cout << "\nAt exit: " << t_head;

    return 0;
}

/*
    Local Variables:
    compile-command: "g++ -std=c++11 -g -O0 ./test.cc -o a.exe && ./a.exe"
    End:
*/

【讨论】:

  • 非常感谢 Tobias(和其他人),我会好好看看这个。我想我已经看到了与 3 相同的行为,你能告诉我为什么它保持相同的 id 吗?我没有删除那条记录吗?
  • @poonam 查看我的编辑。您正在列表中间删除,因此您不会删除最后添加的头部,因此最后一个值为trecord_counttrecord_count 在删除记录时递减,因此再次使用旧值。
猜你喜欢
  • 2021-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-09
  • 1970-01-01
  • 2014-01-31
  • 1970-01-01
相关资源
最近更新 更多