C++中的双链表(指针内存访问违例) 我试图在C++中实现一个双链表。我犯了一个我不明白的错误。这是第行else分支中的运行时错误:

C++中的双链表(指针内存访问违例) 我试图在C++中实现一个双链表。我犯了一个我不明白的错误。这是第行else分支中的运行时错误:,c++,pointers,C++,Pointers,列表->尾部->下一步=新建_节点 它说list->tail位于不同的内存地址。不幸的是,我有Visual Studio的德语版本,所以我不能把它翻译得那么好。该错误被标记为写入访问冲突 有人能解释一下这里发生了什么事吗 包括 typedef结构数据列表节点数据列表节点; struct dlist_node{//表示一个节点及其数据和指向下一个节点的指针,以及 dlist_node*next;//上一个节点 数据列表节点*prev; int数据; }; typedef struct{//表示可以

列表->尾部->下一步=新建_节点

它说list->tail位于不同的内存地址。不幸的是,我有Visual Studio的德语版本,所以我不能把它翻译得那么好。该错误被标记为写入访问冲突

有人能解释一下这里发生了什么事吗

包括 typedef结构数据列表节点数据列表节点; struct dlist_node{//表示一个节点及其数据和指向下一个节点的指针,以及 dlist_node*next;//上一个节点 数据列表节点*prev; int数据; }; typedef struct{//表示可以访问的列表节点 数据列表节点*头部; 数据列表节点*尾部; }数据列表; void dlist_appenddlist*list,int data{//使用新数据/节点追加列表 数据列表节点*新建节点=新建数据列表节点; 新建_节点->数据=数据; 新建节点->下一个=新建节点->上一个=空; 如果列表->尾部==NULL{//如果列表为空 列表->头部=列表->尾部=新节点; } 否则{ list->tail->next=new_node;//此处出现错误 新建节点->上一个=列表->尾部; 列表->尾部=新节点; } } int main{ 数据列表*双链接列表=新数据列表; 标准:库特头; 双链表,42; 标准:库特头; }
如果你还被卡住了,那我们看看能不能把你解开。而C++已经提供了双链表,在写自己的列表时,应该是首选列表实现。也就是说,我们了解到,许多链表自我实施是出于教育目的的练习,无论是出于自学还是作为课堂的一部分。这很好,您需要完全理解它们,有很多遗留代码利用了所有类型的自创列表

如评论中所述,除其他问题外,您最大的问题是在向列表添加节点时没有分配和初始化dlist_节点。您的列表有两个单独的结构,即dlist,其中包含head和tail指针,通过更为封装的方法,这些指针可以成为声明prev和next以及payload数据指针的相同结构或类的成员,并且在列表上操作的函数将是成员函数

使用单独的结构很好,并且在结构中同时使用头指针和尾指针可以确保列表添加可以在O1时间内按顺序完成。虽然您的代码已被编辑,但没有理由为dlist double_linked_list分配。如果你有一个结构,你可以简单地创建一个实例。指针的头部和尾部将指向一个dlist_节点,您可以使用添加到列表中的每个节点来分配和初始化该节点

关键是头部始终指向列表中的第一个节点,尾部始终指向最后一个节点。这提供了按正向和反向迭代列表中每个节点的起点。将节点添加或附加到列表中时,在为每个相关节点正确设置上一个和下一个指针后,如果插入新的第一个节点,只需更新头部,如果相应地添加到列表指针的末尾,则只需更新尾部

当使用一个简单的int作为列表负载时,您可以在addappend节点函数中轻松地分配和初始化新节点。但是,随着有效负载变得越来越复杂,编写createnode函数通常很方便,该函数将完全初始化节点所需的值作为参数,然后分配并完全初始化节点,成功时返回指向新节点的指针,分配失败时返回nullptr。这允许您重用add函数,并且仅为每个新列表自定义createnode

在查看添加和创建节点函数之前,让我们先看看结构本身。在C中,使用TyPulf创建用于结构StdList+StReTistLyNoad的别名是非常方便的,在C++中,这是完全不必要的,并且经常会导致比它解决的问题更多的问题。在C++中声明结构时,只需创建结构体的实例,并可以直接将结构名称作为一种类型引用,例如

struct dlist_node {                         /* list node */
    int data;
    struct dlist_node *prev, *next;
};

struct dlist {                              /* list wrapper with head & tail pointers */
    dlist_node *head, *tail;
};
现在,对于AddYourAppend和createnode函数,可以执行以下操作:

/** create new node initialize all members */
dlist_node *createnode (int v)
{
    dlist_node *node = new dlist_node;      /* allocate node */
    
    if (!node)                              /* validate allocation (and use try/catch) */
        return nullptr;
    
    node->data = v;                         /* initialize members values */
    node->prev = node->next = nullptr;
    
    return node;    /* return new node */
}

/** add node at end of list, update tail to end */
dlist_node *add (dlist *l, int v)
{
    dlist_node *node = createnode (v);      /* allocate node, initialize data */
    
    if (!node)                              /* validate allocation */
        return nullptr;
    
    if (!l->head)                           /* if 1st node, node is head/tail */
        l->head = l->tail = node;
    else {                                  /* otherwise */
        node->prev = l->tail;               /* set prev to tail */
        l->tail->next = node;               /* add at end, update tail pointer */
        l->tail = node;
    }
    
    return node;    /* return new node */
}
始终验证您的分配是否成功,并在分配失败时处理错误

现在,要在主作用域或任何其他需要列表的作用域中创建列表,只需声明一个结构实例,并将head和tail初始化为nullptr,它可以移动到构造函数中,以便自动执行,您可以执行以下操作:

    dlist list = { nullptr, nullptr };          /* initialize list pointers nullptr */
创建名为list的列表。要测试列表,请向列表中添加几个节点,向前和向后检查列表,然后以随机顺序删除所有节点在删除每个节点后检查所有指针,例如

#define NNODES 16
...
    dlist list = { nullptr, nullptr };          /* initialize list pointers nullptr */
    int a[NNODES];                              /* array to shuffle */
    
    for (int i = 0; i < NNODES; i++) {          /* fill array with NNODES int */
        add (&list, i+1);
        a[i] = i+1;
    }
例U se/输出

$./bin/dlist\u dlist\u节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 删除:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 删除:9 2 3 4 5 6 7 8 10 11 12 13 14 15 16 16 15 14 13 12 11 10 8 7 6 5 4 3 2 删除:12 2 3 4 5 6 7 8 10 11 13 14 15 16 16 15 14 13 11 10 8 7 6 5 4 3 2 删除:7 2 3 4 5 6 8 10 11 13 14 15 16 16 15 14 13 11 10 8 6 5 4 3 2 删除:16 2 3 4 5 6 8 10 11 13 14 15 15 14 13 11 10 8 6 5 4 3 2 删除:5 2 3 4 6 8 10 11 13 14 15 15 14 13 11 10 8 6 4 3 2 删除:8 2 3 4 6 10 11 13 14 15 15 14 13 11 10 6 4 3 2 删除:14 2 3 4 6 10 11 13 15 15 13 11 10 6 4 3 2 删除:4 2 3 6 10 11 13 15 15 13 11 10 6 3 2 删除:3 2 6 10 11 13 15 15 13 11 10 6 2 删除:13 2 6 10 11 15 15 11 10 6 2 删除:2 6 10 11 15 15 11 10 6 删除:6 10 11 15 15 11 10 删除:10 11 15 15 11 删除:11 15 15 删除:15 列表为空 内存使用/错误检查

在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有两个责任:1始终保留指向内存块起始地址的指针,以便在不再需要时释放内存块

必须使用内存错误检查程序,以确保您不会试图访问内存或写入超出/超出分配的块的边界,尝试在未初始化的值上读取或基于条件跳转,最后确认释放所有已分配的内存

对于Linux,valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很容易使用,只需运行程序即可

$valgrind./bin/dlist\u dlist\u节点 ==17580==Memcheck,内存错误检测器 ==17580==版权C 2002-2017,GNU GPL'd,由Julian Seward等人。 ==17580==使用Valgrind-3.13.0和LibVEX;使用-h重新运行以获取版权信息 ==17580==命令:./bin/dlist\dlist\u节点 ==17580== 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 删除:15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 16 16 14 13 12 11 10 9 8 7 6 5 4 3 2 1 ... 删除:16 12 12 删除:12 列表为空 ==17580== ==17580==堆摘要: ==17580==在出口处使用:0个块中有0个字节 ==17580==总堆使用率:19个alloc,19个free,分配了74664个字节 ==17580== ==17580==已释放所有堆块-不可能存在泄漏 ==17580== ==17580==对于检测到的和抑制的错误计数,请使用:-v重新运行 ==17580==错误摘要:0上下文中的0个错误被抑制:0上下文中的0 始终确认已释放所有已分配的内存,并且没有内存错误


仔细检查一下,如果还有其他问题,请告诉我。

std::cout head;-您在任何地方都不会初始化dlist中的成员变量。此外,TyPoFeStrut在C++程序中不是必需的——所有您需要的是StuttDList{…};建议:升级参考资料。你正在用新的和IoFrand学习C++,而不是C++。如果你需要手动地用新的工具实现,那么使用带有头、尾部指针和DistySnad结构的DLIST结构是一个很好的方法。但是,在初始化head和tail之后,不要忘记分配和初始化每个dlist_节点。本质上,你在C++中用新的来代替MALOC。你能给我举个例子吗?如何为每个dlist_节点分配内存?我认为数据列表节点*新建节点=新建数据列表节点;我已经这样做了,请参见示例链接。它是C,但唯一的区别是使用new而不是malloc。示例中添加到列表中的函数是add,而不是dlist_append。add函数调用create_node,该节点为一个节点进行分配,并初始化每个节点。
#include <iostream>
#include <random>

#ifndef NNODES
#define NNODES 16
#endif

/*
* non-list misc functions
*/

/** shuffle integer array of size 'n'
*  (using fisher-yates method)
*/
void shuffle (int *a, int n)
{
    std::random_device rd;                        /* random seed */
    std::mt19937 gen(rd());                       /* standard mersenne_twister_engine */
    std::uniform_int_distribution<> dist(0, NNODES - 1);    /* distribution 0, 15 */
    
    int i, tmp;
    while (n-- > 1) {
        i = dist(gen);
        tmp  = a[i];
        a[i] = a[n];
        a[n] = tmp;
    }
}

/*
* list structs and functions
*/

struct dlist_node {                         /* list node */
    int data;
    struct dlist_node *prev, *next;
};

struct dlist {                              /* list wrapper with head & tail pointers */
    dlist_node *head, *tail;
};

/** create new node initialize all members */
dlist_node *createnode (int v)
{
    dlist_node *node = new dlist_node;      /* allocate node */
    
    if (!node)                              /* validate allocation (and use try/catch) */
        return nullptr;
    
    node->data = v;                         /* initialize members values */
    node->prev = node->next = nullptr;
    
    return node;    /* return new node */
}

/** add node at end of list, update tail to end */
dlist_node *add (dlist *l, int v)
{
    dlist_node *node = createnode (v);      /* allocate node, initialize data */
    
    if (!node)                              /* validate allocation */
        return nullptr;
    
    if (!l->head)                           /* if 1st node, node is head/tail */
        l->head = l->tail = node;
    else {                                  /* otherwise */
        node->prev = l->tail;               /* set prev to tail */
        l->tail->next = node;               /* add at end, update tail pointer */
        l->tail = node;
    }
    
    return node;    /* return new node */
}

/** print all nodes in list */
bool prn (dlist *l)
{
    if (!l->head) {
        std::cout << "list-empty\n";
        return false;
    }
    for (dlist_node *n = l->head; n; n = n->next)
        std::cout << ' ' <<  n->data;
    std::cout.put('\n');
    
    return true;
}

/** print all nodes in list in reverse */
bool prnrev (dlist *l)
{
    if (!l->tail) {
        std::cout << "list-empty\n";
        return true;
    }
    for (dlist_node *n = l->tail; n; n = n->prev)
        std::cout << ' ' <<  n->data;
    std::cout.put('\n');
    
    return false;
}

/** delete node with value v from list (for loop) */
bool del_node (dlist *l, int v)
{
    if (!l->head) {
        std::cout << "list-empty\n";
        return false;
    }
    dlist_node **ppn = &l->head;            /* pointer to pointer */
    dlist_node *pn = l->head;               /* pointer to node */
    
    for (; pn; ppn = &pn->next, pn = pn->next) {
        if (pn->data == v) {
            *ppn = pn->next;                /* set node at address to next */
            
            if (pn != l->tail)              /* prev is next prev */
                (*ppn)->prev = pn->prev;
            else                            /* deleting tail, set tail to prev */
                l->tail = pn->prev;
            
            delete pn;                      /* free current */
            pn = nullptr;
            
            break;
        }
    }
    
    return true;
}

/** delete all nodes in list */
void del_nodes (dlist *l)
{
    dlist_node *n = l->head;
    
    while (n) {
        dlist_node *victim = n;
        n = n->next;
        delete victim;
    }
    
    l->head = l->tail = nullptr;
}

int main (void) {
    
    dlist list = { nullptr, nullptr };          /* initialize list pointers nullptr */
    int a[NNODES];                              /* array to shuffle */
    
    for (int i = 0; i < NNODES; i++) {          /* fill array with NNODES int */
        add (&list, i+1);
        a[i] = i+1;
    }
    shuffle (a, NNODES);                        /* shuffle array for random removal */
    
    prn (&list);                                /* print list forward */
    prnrev (&list);                             /* print list reverse */
    std::cout.put('\n');
    
    for (int i = 0; i < NNODES; i++) {          /* remove all nodes in random order */
        std::cout << "deleting : " << a[i] << '\n';
        del_node (&list, a[i]);                 /* delete node with random value a[i] */
        
        if (prn (&list)) {                      /* print list forward if nodes remain */
            prnrev (&list);                     /* print list reverse if nodes remain */
            std::cout.put('\n');                /* tidy up with a '\n' */
        }
    }
}