C++实现LeetCode两数相加:链表指针错误排查与优化
嘿,我来帮你拆解下你遇到的问题,顺便给你补补C++指针的关键知识点,以及链表构建的正确姿势:
你遇到的UndefinedBehaviorSanitizer错误,根源出在链表节点的内存管理上,咱们看这段代码:
while (sum/10!=0) { ListNode nextNode = ListNode(sum%10); currentNode.next=&nextNode; currentNode = nextNode; sum /= 10; }
这里的nextNode是栈上的局部变量——栈内存的生命周期只限于当前循环块,每次循环结束后,这个变量占用的内存就会被系统回收。当你把currentNode.next指向它的地址时,一旦循环迭代结束,这个地址就变成了无效内存,后续访问这个指针就是典型的悬空指针操作,直接触发未定义行为。
另外你开头的currentNode也是栈上变量,sumNodePtr指向它的地址,等addTwoNumbers函数执行完毕返回时,这个栈变量同样会被销毁,返回的指针依然是悬空指针,这也是致命问题!
你的listToNum函数用int存储转换后的数字,但LeetCode的测试用例里,链表可能长达几十位,远远超出int的取值范围(通常是-231到231-1),直接会导致整数溢出,计算结果完全错误。而且pow(10, tenPower)是浮点运算,当指数较大时,会出现精度损失——比如pow(10,3)可能返回999.9999999999999,转成int就变成999,直接导致计算错误。
解决指针问题的核心是:用new动态分配堆内存来创建链表节点。堆内存的生命周期由你控制,不会随着函数或循环结束而销毁(LeetCode判题系统会自动处理内存释放,不用你手动delete)。
另外,正确的两数相加逻辑应该是逐位相加并处理进位,而不是先转成整数再相加,这样从根源上避免了溢出问题。
给你修改后的完整可运行代码:
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { // 用哑节点(dummy node)简化链表头部的边界处理 ListNode* dummy = new ListNode(0); ListNode* current = dummy; int carry = 0; // 记录进位 // 只要还有未处理的节点,或者还有进位,就继续循环 while (l1 != nullptr || l2 != nullptr || carry != 0) { // 取当前位的数值,节点为空则取0 int val1 = l1 ? l1->val : 0; int val2 = l2 ? l2->val : 0; // 计算当前位的总和与新的进位 int sum = val1 + val2 + carry; carry = sum / 10; int currentDigit = sum % 10; // 动态创建新节点并链接到当前链表尾部 current->next = new ListNode(currentDigit); current = current->next; // 移动原链表的指针 if (l1) l1 = l1->next; if (l2) l2 = l2->next; } // 哑节点的下一个节点就是结果链表的头 ListNode* result = dummy->next; delete dummy; // 释放哑节点的内存(可选,LeetCode不强制要求) return result; } };
- 栈内存 vs 堆内存:栈上的局部变量(比如你代码里的
currentNode、nextNode)生命周期仅限于当前作用域(函数/循环块),离开作用域后内存被回收,指向它们的指针就变成悬空指针,访问悬空指针是未定义行为,可能导致程序崩溃或诡异的错误。堆内存(用new分配的)生命周期由程序员控制,直到delete才会释放,适合存储需要在作用域外使用的数据(比如链表节点)。 - 哑节点的妙用:在链表操作中,哑节点是一个临时的头节点,用来避免处理链表为空的边界情况——比如结果链表一开始没有节点时,不需要额外判断头指针是否为空,直接把新节点链接到哑节点后面即可,最后返回哑节点的
next指针就是结果,非常省心。 - 大数处理思路:对于链表表示的大数运算,永远不要尝试转成普通整数类型存储,直接逐位处理才是正确的思路,既能避免溢出,也符合题目设计的初衷。
你的核心问题是误用了栈上的局部变量作为链表节点,导致悬空指针;另外转整数的思路本身存在溢出和精度问题。改用逐位相加+动态分配节点的方式,就能完美解决这些问题,同时也更贴合LeetCode题目的考察点。
内容的提问来源于stack exchange,提问作者decisions

