微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++中指向指针赋值和解引用的指针

如何解决C++中指向指针赋值和解引用的指针

假设我有一个如下所示的链表节点:

struct ListNode {
  int val;
  ListNode *next;
  ListNode(int x) : val(x),next(NULL) {}
};

目标是编写一个函数删除单向链表中的节点。在恒定时间内做到这一点的一种有效方法是这样的:

void deleteNode(ListNode* node) {
    auto *tmp = node->next;
    *node = *tmp;
    delete tmp;
}

这是可行的,但为什么我们需要取消引用指针?

如果node一个指针而tmp一个指针,为什么需要解引用?为什么我不能做node = tmp

解决方法

执行 *node=*tmp 时,您将 *tmp 的所有字节复制到 *nodenode->val 现在持有 tmp->valnode->next 现在持有 tmp->next

node 的旧内容已被遗忘(因为您想摆脱此节点,这很正常)但您将下一个节点的内容保存在同一位置。 然后,如果您删除下一个节点(通过 tmp 已知),您不会丢失其内容(它已保存在前一个节点中)。

,

让我们分解一下 deleteNode 函数的三行:

    auto *tmp = node->next;

这将创建一个局部变量 tmp,它将是传递的 next 参数的 node 字段的副本。这是指向列表中下一个结构的指针,一旦我们复制了它,我们就可以删除或覆盖该成员。

    *node = *tmp;

这会将tmp(即列表中的下一个节点)指向的结构的实际数据复制到当前 节点,因此会覆盖 next 字段。我们需要取消引用两个指针以复制指向的结构的值。

    delete tmp;

这将删除给定列表中的“下一个”节点。但是,我们已经将其所有的数据(包括其 next 成员)复制到我们当前的节点中,因此我们修改后的列表现在(实际上)从原始列表中的第二个开始(即传递参数);值得注意的是,next*node 字段现在将是最初存储在 node->next->next 中的地址——因此,我们“跳过”了列表中的一个条目(第二个)并将其删除。

,

你不能只写 node = tmp 的原因是因为它不会改变你函数之外的任何东西。

给定这个链表

node0 -> node1 -> node2 -> node3

如果你想删除 node1,想要的结果是

node0 -> node2 -> node3

如果不想主动修改next中的指针值(即地址node0),必须将node2里面的值移动到{{ 1}} 是。

,

为什么我不能做 function myFunction() { var spreadSheet = SpreadsheetApp.getActiveSpreadsheet(); var values = spreadSheet.getDataRange().getValues(); values.shift(); var stocks = []; var emails = ['test@gmail.com']; var emailSubject = 'PRICE ALERT'; var emailBody = 'The following tickers reached its target price:'; values.forEach(function (stockArray,index) { // ticker,CURRENT price,comparison symbol,TARGET price var [stockTicker,stockCurrent,stockSymbol,stockTarget,dates] = stockArray; var dateToday = convertDate(new Date()); // Make string into array and removes all blank elements var dateArray = dates.toString().split(",").filter(date => date); // Properly format dates dateArray.forEach(function (date,index){ dateArray[index] = convertDate(new Date(date)); }); // If current price is <stockSymbol> than target and dateToday isn't in column E if (eval(stockCurrent + " " + stockSymbol + " " + stockTarget) && !dateArray.includes(dateToday)) { // Take note of the tickers so we can send in 1 email stocks.push(stockTicker); // Add dateToday to dateArray if date is new dateArray.push(dateToday); // Set dateArray as string as value to column E spreadSheet.getRange("E" + (index + 2)).setValue(dateArray.join(",")); } }); // Only proceed to mail if we had a new valid ticker that satisfies the condition if(stocks.length > 0) { // Join all stocks in one email per user,send as 1 email emailBody = emailBody + "<br> - " + stocks.join("<br> - "); emails.forEach(function (email) { MailApp.sendEmail(email,emailSubject,'',{ htmlBody: emailBody }); }); } // Create trigger after every run (will trigger every minute) createTrigger(); } function convertDate(date){ // Convert date to proper timezone return Utilities.formatDate(date,SpreadsheetApp.getActive().getSpreadsheetTimeZone(),"MM/dd/YYYY"); } function createTrigger(){ // Delete all existing triggers before creating one // Ensuring none will exist before creating trigger. var triggers = ScriptApp.getProjectTriggers(); triggers.forEach(function (trigger){ ScriptApp.deleteTrigger(trigger); }); // Create trigger after every run which is per minute ScriptApp.newTrigger('myFunction') .timeBased() .after(60 * 1000) .create(); }

你可以这样做,但它不会做任何有用的事情。 node = tmpnode 中的局部变量。由于它是一个指针,您可以使用该本地指针来修改它指向的内容,但修改指针本身在函数之外没有任何影响。

实际上指针在这方面没有什么不同。当你有

deleteNode

传递引用是不同的:

void foo(int x) {
    x = 42;
}

与指针相同:

void bar(int& x) {
    x = 42;
}
int a = 0;
bar(a);    // now a == 42 
,

你的函数真正做的是,它不会从参数中删除节点,而是下一个节点,用跟随者覆盖当前节点。

指针的解除引用就像一个 memcpy() 并将数据从下一个节点移动到当前节点。您不是在复制指针,而是在复制它指向的数据。

这样你就可以用相同的节点指针重复调用函数,它会沿着链向下移动。 但是,由于您没有检查指针,因此最后一个节点可能有一个 NULL 指针,并且会在取消引用时崩溃。

所以你需要做

 if (tmp)
     *node = *tmp;

示例:

typedef struct list
{
    struct list *next;
    int value;
} List;

void deleteNext(List* node)
{
    auto *tmp = node->next;

    if(tmp)
        *node = *tmp;

    delete tmp;
}
int main(int argc,char *argv[])
{
    List *l0 = new List;
    List *l1 = new List;
    l0->value = 0;
    l0->next = l1;
    l1->value = 1;
    l1->next = NULL;

    deleteNext(l0);
    deleteNext(l0); // Without the 'if' it will crash here.

    return 0;
}
,

如果您执行 node = tmp 并在删除 tmp 之后,您将删除 ListNode 指向的 node

,
  1. 正如其他人指出的那样,node = tmp 只是改变了参数(局部变量)
  2. *node = *tmp 是复制 ListNode 的内容,相当于 node.val = tmp.val; node.next = tmp.next
  3. 这个函数实际上删除了下一个元素 - 它可以工作,但它使下一个指针无效(如果有东西将 node->next 引用为指针,它现在是一个悬空指针)
,

但是为什么我们需要取消对指针的引用?

让我们看看如果我们不通过指针间接会发生什么:

auto *tmp = node->next;
node = tmp;
delete tmp;

这相当于只是

delete node->next;

// resulting structure
previous    node             next (deleted)     next next (leaked)
1---------->2----dangling--->_                  4

// desired structure that we get from the correct code
previous    node             next (deleted)     next next
                             _
1-----------3---------------------------------->4

因此,我们最终删除了错误的节点,并且在应该删除的节点中出现了一个悬空指针。


请注意,尝试删除最后一个节点时,即使是正确的初始化版本也会被破坏。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?