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