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

C#中链表赋值后修改原变量为何不影响新变量?栈堆解析

链表合并方法中指针指向变化的原因与内存执行过程分析

问题场景

在实现链表合并的MergeTwoLists方法中,将方法参数list1赋值给局部变量l1,初始时二者均指向链表1->2->4->null。但首次进入while循环执行list1 = list1.next后,list1变为指向2->4->null,l1却仍指向原链表。以下是对该现象的原因分析,以及栈、堆内存的具体执行过程。

完整代码

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LinkedList
{
     public class ListNode
    {
        public int val;
        public ListNode next;
        public ListNode(int val = 0, ListNode next = null)
        {
            this.val = val;
            this.next = next;
        }
     }


    internal class Program
    {
        static void Main(string[] args)
        {
            ListNode list1= new ListNode(1, new ListNode(2, new ListNode(4, null)));
            ListNode list2 = new ListNode(1, new ListNode(3, new ListNode(4, null)));
            ListNode list3 = MergeTwoLists(list1,list2);
        }

        public static ListNode MergeTwoLists(ListNode list1, ListNode list2)
        {
            if (list1 == null)
                return list2;
            else if (list2 == null)
                return list1;
            ListNode l3 = new ListNode(0,null),l1=list1,l2=list2;
            ListNode l4 = l3;
            while (list1 != null && list2 != null)
            {                               
                if (list1.val <= list2.val)
                {
                    l4.next = list1;
                    list1 = list1.next;
                }
                else
                {
                    l4.next = list2;
                    list2 = list2.next;
                }
                l4 = l4.next;
            }
            l4.next=list1==null?list2:list1;
            return l3.next;
        }
    }
}

核心原因

C#中引用类型的变量本质是存储堆对象地址的指针,变量赋值操作是复制这个地址值,而非让两个变量共享同一个存储空间:

  • 执行l1 = list1时,是把list1栈内存中存储的堆节点地址,复制到l1的栈内存中,此时二者指向同一个堆节点。
  • 执行list1 = list1.next时,是取出当前list1指向节点的next属性(即下一个堆节点的地址),并将这个新地址覆盖list1栈内存中的旧地址。这个操作只修改了list1变量的存储值,不会影响l1栈内存中已保存的原地址,因此l1依然指向原链表头节点。

栈、堆内存执行过程

  1. 方法调用时的栈初始化
    调用MergeTwoLists方法时,栈上会为方法的参数(list1、list2)和局部变量(l3、l1、l2、l4)分配空间,这些变量都是指针,存储堆中ListNode对象的内存地址。

  2. 初始赋值阶段

    • 参数list1的栈内存中,存储着堆中第一个ListNode(val=1,next指向val=2节点)的地址。
    • 执行l1 = list1后,l1的栈内存被写入和list1相同的地址,此时二者都指向堆中的同一个链表头节点。
  3. 循环内的指针修改
    进入while循环执行list1 = list1.next:

    • 先读取list1栈内存中的地址,找到堆中对应的ListNode,取出其next属性(即val=2节点的堆地址)。
    • 将这个新地址覆盖list1的栈内存值,此时list1的栈值变为val=2节点的地址,而l1的栈值仍保留原val=1节点的地址,因此l1指向的链表没有变化。
  4. 堆内存的状态
    整个过程中,堆中的ListNode对象(链表节点)本身没有被修改,所有操作只是改变了栈上指针变量的指向,堆中的链表结构始终保持初始状态(1->2->4->null和1->3->4->null)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 08:33:25