You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
    }
};
关键C++指针知识点梳理
  • 栈内存 vs 堆内存:栈上的局部变量(比如你代码里的currentNode、nextNode)生命周期仅限于当前作用域(函数/循环块),离开作用域后内存被回收,指向它们的指针就变成悬空指针,访问悬空指针是未定义行为,可能导致程序崩溃或诡异的错误。堆内存(用new分配的)生命周期由程序员控制,直到delete才会释放,适合存储需要在作用域外使用的数据(比如链表节点)。
  • 哑节点的妙用:在链表操作中,哑节点是一个临时的头节点,用来避免处理链表为空的边界情况——比如结果链表一开始没有节点时,不需要额外判断头指针是否为空,直接把新节点链接到哑节点后面即可,最后返回哑节点的next指针就是结果,非常省心。
  • 大数处理思路:对于链表表示的大数运算,永远不要尝试转成普通整数类型存储,直接逐位处理才是正确的思路,既能避免溢出,也符合题目设计的初衷。
总结

你的核心问题是误用了栈上的局部变量作为链表节点,导致悬空指针;另外转整数的思路本身存在溢出和精度问题。改用逐位相加+动态分配节点的方式,就能完美解决这些问题,同时也更贴合LeetCode题目的考察点。

内容的提问来源于stack exchange,提问作者decisions

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 23:37:28