0

编辑

为简单起见:我只想制作最基本的可能光标,它只是通过我的列表而不以任何方式/形状或形成我的“开始”。我仍然希望在我返回某些东西时开始改变,而不是以前。那么是否可以创建一个指针来开始,遍历列表而不更改任何内容,除了在最后我想添加一个新节点时?

我也可以用一个不是“节点”的简单指针浏览列表吗?

/编辑

我有一个简单的(单独的)链表作为我作业的一部分。当然,除此之外我还有很多事情要做,但是在我把列表排除在外之后,一切都应该向前发展,但是当我使用 C++ 一段时间(它是 borland C++)时,我做了很多知道要么被遗忘了一半,要么已经过时。我已经在 python 中编程了一段时间,但这意味着我一直对指针在 C++ 中的工作方式感到沮丧。

我的问题是当我尝试向列表中添加一个新节点时,我的光标行为异常,我将在下面解释:

编辑:好的,我改变了:

Node *cursor;
cursor = new Node
cursor = begin;

惨败,但结果是一样的,在声明光标和开始之后都指向相同的内存位置(类似于:0x32ce8)。

/编辑

Node *add_node (Node *begin,string type, int sum, int ap_nr) // begin is the first node in the list
{
    // if first node is dummy node

    if (begin->ap_nr == -1)
        {
            begin->type = type;
            begin->ap_nr = ap_nr;
            begin->sum = sum;
            begin->next = 0;
            return begin;
        }

    // else create new node and insert it in sorted position

   else
    {
        // EDIT:
        Node *cursor = begin; // Same problem

        //if node should be inserted before first node (begin)

        if (ap_nr <begin->ap_nr)
        {
            cursor->ap_nr = ap_nr;
            cursor->type = type;
            cursor->sum = sum;
            cursor->next = begin;
            return cursor;
        }

当我调试时,begin 总是有一个相似的形式:0x32ce02,当我创建我的“光标”时,它有一个完全不同的形式(也更长),但是当我这样做时:cursor = begin,然后光标变成这样的 0x32df02。

但是问题是当我到达“if (ap_nr ap_nr)”时,绝对没有可行的原因,光标变为:0x32ce02 和“cursor -> next = begin”确保无限循环。而且无论我添加多少节点,这总是会发生,所以每当我打印列表时,它都是最后添加的节点的无限流。

难道我做错了什么 ?是声明还是分配,创造?某物 ?

另外,如果我在另一个模块的某个地方有一个指针 *begin,并且使用这个函数我返回一个新的 begin ... 那应该可以工作,对吗?

PS我也很欣赏一个简单的计数器解决方案(如果我的不好,另一种方法)

另外我应该指出我是如何制作我的清单的。这只是一个简单的节点链接:

struct Node {
    string type;
    int ap_nr;
    int sum;
    Node *next;
};
4

3 回答 3

2

这段代码:

cursor = new Node;
cursor = begin;

执行以下操作:

  • 在堆栈上创建一个新的 Node 对象
  • 将该对象的地址放入变量中current
  • begin立即用包含的地址覆盖该地址

即它所做的只是泄漏一个新的Node. cursorbegin在此之后指向同一件事。

因此该行:

cursor->next = begin;

做一个循环。cursor->next == begin == cursor.

删除该cursor = begin;行并返回光标将执行您想要的操作。创建的节点new Node将是列表的新头,begin指向函数开头的节点将是该列表中的第二个节点。

现在,当您想要遍历(而不是添加到)该列表时,您可以执行以下操作:

 Node *cursor = begin; // assuming begin is the head of your list
 while (cursor != 0) {
    // process this node
    cursor = cursor->next;
 }

假设您已正确构建它,它将访问列表中的每个节点。

几点注意事项:

  • 调用此函数的代码应该类似于:

    list = add_node(list, ...);
    

    如果你没有那个,你就得不到你想要的。

  • 如果您在中看到的地址begin看起来与返回的地址大不相同,则new可能做错了什么,即begin可能指向堆栈地址。不要将堆栈中的对象放在列表中,请确保您都使用new. (如果begin指向全局静态变量,那很好 - 但请确保你不这样做delete)。

  • 除非您实际上需要一个虚拟节点,否则我建议您也将其删除 - 它会使您的add_node功能比它需要的更复杂。

这是没有那个特殊的虚拟节点(不完整的代码)的情况下如何做到这一点的框架:

Node *add_node(Node *list, ...)
{
  Node *new_node = new Node;
  new_node->next = list;
  // fill in other properties
  return new_node;
}

并使用它:

int foo()
{
    Node *list = 0; // or nullptr for C++11
    list = add_node(list, ...); // add item 1
    list = add_node(list, ...); // add item 2
    list = add_node(list, ...); // add item 3
    ...
于 2012-03-31T08:47:29.500 回答
1

指针cursorbegin指向相同的内存位置,因为您明确地这么说:Node* cursor = begin; 字面意思是“创建一个名为的指针变量cursor,它指向与 . 相同的位置begin。” 因此,它确实如此也就不足为奇了。

编辑:根据错误猜测代码的用途删除了建议,并将其更改为更适用的建议

从评论中,我现在了解到您想在某个位置插入一个节点,以便该字段ap_nr在结果列表中增加,假设它最初是增加的(如果这仍然不正确,请清楚说明您想要什么) .

对于这种情况,cursor现在的初始化当然是正确的。但是,修改对象指向是正确的:您想在该节点之前插入一个节点。但为此,您必须进行一些更改:cursor

首先,您需要另一个指针变量,它保存一个指向新创建节点的指针,例如

Node* new_node = new Node;

然后你必须将该节点插入到列表中。也就是说,cursor->ap_nr=ap_nr;您必须使用etc 而不是new_node->ap_nr=ap_nr;etc。此外,它后面的节点当然不是列表的第一个节点(由 指向first),而是您刚刚找到的那个(由 指向current)。

但是,现在您遇到了一个问题:您必须将该新节点插入到列表中,这意味着您必须修改前一个next节点的指针(但不是指向,而是指向新创建的节点!)。但是您不再有指向前一个节点的指针,因为您的列表是单链接的,也就是说,您没有从找到的元素到前一个元素的指针。但是要插入元素,您必须更改.beginnext

但是,您拥有的是指向下一个节点的指针。因此,更好的策略是将您的cursor点指向前一个节点,然后始终使用cursor->next而不是cursor(当然,移动时除外cursor)。这样你就可以,你设置之后new_node->next,写cursor->next = new_node;

您的代码中缺少的其他内容current是不为空的检查(它将位于列表的末尾)和代码实际上推动了您的cursor前进(属于else您的一部分 inner if)。

实际上,我现在注意到您的块没有关闭,因此前进代码可能存在于您的实际代码中。

最后,一些一般性建议:如果您将函数的代码模块化,您可能会更轻松地编写该代码:让一个函数在给定的节点之后插入一个新节点(仅更改next指针,并返回一个指向新插入的代码),有另一个函数来查找应该插入新节点的节点,并且让你的函数add_node只调用那些其他函数。这样,在每个功能中,您都可以专注于其中一个子问题。

于 2012-03-31T09:27:43.917 回答
1
    Node *cursor;
    cursor = new Node;
    cursor = begin;

在这里准确了解您对这 3 行所做的操作 - 可能发生的事情比您实际意识到的要多。

  • 在第一行,您声明了一个名为cursor的新变量——这个变量是一个指针对象的名称,它的类型为Node*。(简单来说,Node* 的意思是“指向节点的指针”)。
    存储在指针对象中的数据类型只是数字整数数据,恰好代表内存中的地址。指针对象实际上与任何其他类型的整数对象没有什么不同。
    (注意“变量”和“对象”之间的区别——对象是存储在内存中的东西,例如数字/字符/内存地址,变量是对象的名称

  • 在第二行,发生了两件事——首先你为Node对象分配内存并在分配的内存中构造一个新的Node对象——这个内存没有变量名,它只是一个自由存储对象(这是new Node是如何工作的),然后使用 = 运算符(赋值运算符)将新分配的Node对象的地址值(一个数字)存储在游标变量中。
    存储新 Node 对象的地址值后,访问新分配的 Node 对象的唯一方法是通过游标变量名。

  • 在第三行,您立即覆盖游标变量存储的值;它不是存储新节点的地址数据,而是存储另一个对象的地址(从另一个名为begin的指针变量复制的另一个对象的地址)。这会立即导致新分配的节点“泄漏”。

我试图表达的观点(我相信您可能没有意识到)是在这三行代码中可能需要考虑四个不同的对象(即“内存中的项目”)。其中两个是称为cursorbegin的指针对象(整数),另外两个是没有名称的自由存储对象(节点),但可以使用指针对象存储的地址值访问。

您可能会发现这里有很多值得一读的链接:

于 2012-03-31T09:13:04.073 回答