如何解决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
的所有字节复制到 *node
中
node->val
现在持有 tmp->val
,node->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 = tmp
是 node
中的局部变量。由于它是一个指针,您可以使用该本地指针来修改它指向的内容,但修改指针本身在函数之外没有任何影响。
实际上指针在这方面没有什么不同。当你有
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
。
- 正如其他人指出的那样,
node = tmp
只是改变了参数(局部变量) -
*node = *tmp
是复制ListNode
的内容,相当于node.val = tmp.val; node.next = tmp.next
- 这个函数实际上删除了下一个元素 - 它可以工作,但它使下一个指针无效(如果有东西将
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 举报,一经查实,本站将立刻删除。