Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
引发异常:写入访问冲突。上一个ptr为0x4 我正在学习链表,我决定尝试在C++中实现链表。_C++_Class_Linked List - Fatal编程技术网

引发异常:写入访问冲突。上一个ptr为0x4 我正在学习链表,我决定尝试在C++中实现链表。

引发异常:写入访问冲突。上一个ptr为0x4 我正在学习链表,我决定尝试在C++中实现链表。,c++,class,linked-list,C++,Class,Linked List,我创建了一个具有int val和Node*ptr属性的Node类。 然后我创建了一个带有属性first\u节点的链表类,构造函数可以工作 append函数将节点“追加”到列表中,就像Python中一样。我首先想到的是将ptr作为对节点指针的引用,然后在其为null时对其进行更改,但一旦引用,就无法更改为引用任何其他变量,因此我创建了另一个变量prev_ptr,该变量指向节点的指针,使其成为节点** 每个循环,它检查ptr是否为NULL,如果不是,ptr和prev_ptr分别更新为下一个节点的指针

我创建了一个具有int val和Node*ptr属性的Node类。 然后我创建了一个带有属性first\u节点的链表类,构造函数可以工作

append函数将节点“追加”到列表中,就像Python中一样。我首先想到的是将ptr作为对节点指针的引用,然后在其为null时对其进行更改,但一旦引用,就无法更改为引用任何其他变量,因此我创建了另一个变量prev_ptr,该变量指向节点的指针,使其成为节点**

每个循环,它检查ptr是否为NULL,如果不是,ptr和prev_ptr分别更新为下一个节点的指针值和下一个节点指针值的地址

这会一直发生,直到它找到空指针,然后将其更改为输入节点的地址

但我得到了一个错误,说:

引发异常:写入访问冲突。上一个ptr为0x4

我想不出是怎么回事

课程:

包括 包括 包括 类节点{ 公众: int-val; 节点*ptr=nullptr; Nodeint Val=NULL{ val=val; } }; 类链表{ 公众: 节点第一个节点; 链接列表{ 节点fF; 第一个_节点=f; } 节点和元素无效{ Node*ptr=first_Node.ptr; 节点**prev_ptr=&first_Node.ptr; 虽然是真的{ 如果ptr==nullptr{ *prev_ptr=&元素; 打破 } ptr=*ptr.ptr; prev_ptr=&*ptr.ptr; } } }; 主要

int main{ 链表清单5; 节点3; 清单3; 节点4; 清单4; 返回0; } 首先,将ptr推进到下一个节点。然后,您再次使用ptr,忘记了它已经是高级的:*ptr.ptr现在指向两个向前的节点,我们不知道是否可以走那么远

也许你需要交换作业

            prev_ptr = &((*ptr).ptr);
            ptr = (*ptr).ptr;
此外,为什么不进行ptr->ptr?

编码和解释 我认为这段代码应该也能工作,所以不需要有两个指针。这是基于Linus Torvalds在一次采访中给出的

void append(Node &element)
{
    Node** cursor = &first_node.ptr;
    while ((*cursor) != nullptr)
        cursor = &(*cursor)->ptr;
    *cursor = &element;
}
它消除了对多个指针的需要,消除了边缘情况,并允许我们评估while循环的条件,而不必松开指向下一个元素的指针。这允许我们修改指向NULL的指针,并使用一个迭代器,而不是ptr和prev_ptr

命名约定 另外,规范是调用链表头中的第一个节点,并调用指向下一个节点的指针,而不是ptr,因此我将在下面的代码中重命名它们

void append(Node &new)
{
    Node** cursor = &head.next;
    while ((*cursor) != nullptr)
        cursor = &(*cursor)->next;
    *cursor = &new;
}

好吧,你做了一些奇怪的事情。首先,您的链接列表可能没有firstNode的节点。它应该有一个节点*。毕竟,空列表是可能的。因此,通常会删除第一个节点。此外,还有一个非正式的命名惯例,称它为头。还有一个标准约定,即在下一个节点中调用链接,而不是ptr

但是对于append方法,有两个更简单的方法。首先,您还可以在链接列表中保留一个节点*尾部。这是常见的。它指向列表中的最后一个节点。如果执行此操作,则“附加”如下所示:

void append(Node &nodeToAppend) {
    if (head == nullptr) {
        head = &nodeToAppend;
        tail = &nodeToAppend;
    }
    else {
        tail->next = nodeToAppend;
        tail = &nodeToAppend;
    }
}
但是,能够在任意位置插入或附加而不带尾部也是值得的:

 void append(Node &nodeToAppend) {
     if (head == nullptr) {
         head = &nodeToAppend;
     }
     else {
         Node *ptr = head;
         while (ptr->next != nullptr) {
              ptr = ptr->next;
         }
         ptr->next = &nodeToAppend;
     }
 }
以某种排序顺序插入的内容几乎相同,尽管略有不同。while循环看起来像:

         while (ptr->next != nullptr && ptr->value < nodeToAppend.value) ...

但在其他方面是相同的。

此代码并不能解决您当前的问题,但回答了评论中提出的一个问题

链表通常是我教300-400级作业的地方。要写出一个像样的链表,有很多原则是必须具备的。首先,我将显示main.cpp及其输出

main.cpp

include list.hpp 包括 样板 void printContainer&container,std::ostream&sout=std::cout { 汽车i:集装箱{ sout数据; m_尾=m_头; m_大小=1; Node*walker=other.m_head->next; 当沃克{ 推送_backalker->data; ++m_尺寸; 沃克=沃克->下一步; } } 样板 列表::列表列表和其他:列表{ 交换*这个,其他; } 样板 列表::~List{ 清楚的 } 样板 无效列表::push_frontT val { 如果!你的头{ 先做个评估; 回来 } Node*tmp=newnodeval; tmp->next=m_头; m_head->prev=tmp; m_头=tmp; ++m_尺寸; } 样板 无效列表::push_backT val{ 如果!你的头{ 先做个评估; 回来 } Node*tmp=newnodeval; tmp->prev=m_tail; m_tail->next=tmp; m_tail=tmp; ++m_尺寸; } 样板 类型名称列表::迭代器列表::开始{ 返回迭代器 D } 样板 类型名列表::迭代器列表::结束{ 返回iteratornullptr; } 样板 类型名列表::迭代器列表::findT val{ 返回迭代器find_nodeval; } 样板 标准::大小列表::大小常量{ 返回m_大小; } 样板 typename列表::迭代器列表::擦除typename列表::迭代器toErase { Node*Node=find_Node*toErase; 如果节点->上一个{ 节点->上一个->下一个=节点->下一个; }否则{ m_head=节点->下一步; } 如果节点->下一步{ 节点->下一步->上一步=节点->上一步; }否则{ m_tail=node->prev; } 节点*toReturn=节点->下一步; 删除节点; 回归回归; } 样板 无效列表::清除{ 节点*tmp=m_头; 当你的头{ m_头=m_头->下一步; 删除tmp; tmp=m_头; } m_tail=nullptr; m_大小=0; } 样板 布尔列表::运算符=列表其他{ 交换*这个,其他; 归还*这个; } 样板 无效列表::先生成节点值{ m_头=新节点评估; m_尾=m_头; m_大小=1; } 样板 类型名称列表::节点*列表::查找节点集值{ 如果!你的头{ 返回空ptr; } 节点*walker=m_头; while walker!=nullptr&&walker->data!=val{ 沃克=沃克->下一步; } 返回步行者; } 样板 空心岩屑层和左侧、列表和右侧{ 使用std::swap; swaplhs.m_头、rhs.m_头; Swapllhs.m_尾、rhs.m_尾; swaplhs.m_尺寸、rhs.m_尺寸; } /* *列表迭代器实现 */ 样板 列表::迭代器::迭代器节点*节点:m_posnode{} 样板 T&List::迭代器::运算符*{ 返回m_pos->data; } 样板 类型名列表::迭代器&列表::迭代器::运算符++{ m_pos=m_pos->next; 归还*这个; } 样板 类型名称列表::迭代器列表::迭代器::运算符++int{ 迭代器tmpm_pos; ++*这,; 返回tmp; } 样板 类型名称列表::迭代器&列表::迭代器::运算符-{ m_pos=m_pos->prev; 归还*这个; } 样板 类型名列表::迭代器列表::迭代器::运算符int{ 迭代器tmpm_pos; -*这,; 返回tmp; } 样板 布尔列表::迭代器::运算符==常量迭代器和其他{ 返回m_pos==其他m_pos; } 样板 布尔列表::迭代器::运算符=常量迭代器和其他{ return!*this==其他; } 恩迪夫
使用调试器逐步完成程序。留心坏地址的来源。这几乎总是比问SO问题快。提示:当您更新prev_ptr时,请记住您已经在它之前更新了ptr。节点应该对用户不可见。我想将整数添加到列表中。不要在堆栈上创建节点,然后将节点添加到我的列表中。注意:您可以使用ptr->/*something*/而不是*ptr./*something*/。@HaydenSoares链接列表节点通常是*动态分配的,例如使用new。这将解决在本地创建节点变量的问题。当你使用完它们后,你只需要删除它们,即在list类的析构函数和赋值运算符中。完全同意。更好的方法,但作为一个答案,它需要一点肉来解释你在做什么和为什么。否则,人们会出现并复制它,而不真正了解它的功能和工作原理。世界已经够多了。@user4581301你完全正确!我现在已经解释了我的代码并修复了一些问题。我还测试了它,它似乎很有效。关于它的工作原理有一点额外的解释:使用指向ptr的指针可以将先前的插入点prev_ptr与当前指针结合起来。这还可以抽象出指针之间的所有差异,这些指针具有相同的用途,但具有不同的名称,如head和ptr,并且可以消除处理不同名称的大量冗余代码。对于非常规名称和实现,在执行此操作之前,我只知道链表包含包含值和指向下一个节点的指针的节点。在不知道如何实际实现某件事情之前尝试实现它是否被认为是不好的做法?@HaydenSoares No。我只是想帮助你改进和遵守你在你的水平上不应该知道的惯例。
         while (ptr->next != nullptr && ptr->value < nodeToAppend.value) ...
1 2 3 4 5 6 7 8 9 10 
1 2 3 5 6 7 8 9 10 
2 3 5 6 7 8 9 10 
2 3 5 6 7 8 9