在c#中使用结构体实现链表时获取垃圾值(不安全结构)

本文关键字:不安全 结构 获取 结构体 链表 实现 | 更新日期: 2023-09-27 18:04:41

我试图验证我的一个朋友报告的行为,所以我在c#中使用struct实现单链表。因为我必须使用指针,所以我使用了c#提供的不安全结构。我还将项目属性的build选项卡中的属性设置为Allow unsafe code,以使代码可编译。

这是我完整的代码实现:

public unsafe struct NodeList
{
    public Node* head;
    public Node* current;
    public  void AddNode(int d)
    {
        Node n = new Node();
        n.data = d;
        n.link = null;
        if (head == null)
        {
            head = current = &n;
        }
        else
        {
            (*current).link = &n;
            current = &n;
        }
        Console.WriteLine(head->data);
    }
    public void TraverseNodes()
    {
        Node* temp = head;
        while(temp != null)
        {
             Console.WriteLine(temp -> data);
             temp= temp -> link;                
        }
    }
}
public unsafe struct Node
{
    public int data;
    public Node* link;
}
class Program
{
    private static void UnsafeDSImplementation()
    {
        var myLinkedList = new NodeList();
        myLinkedList.AddNode(2);
        myLinkedList.AddNode(4);
        myLinkedList.TraverseNodes();
    }
    static void Main(string[] args)
    {
        UnsafeDSImplementation();
    }
}

的观察:

  1. 每次我进入AddNode方法并尝试打印节点数据值时,第一次我得到2,第二次我得到4。我想知道当我在添加第一个节点时只分配一次时,头部是如何变化的。
  2. 在遍历所有节点时-我获得第一个节点的数据值为2243610(这在每次运行时都在变化,所以它是垃圾)和System.NullRefernceException异常用于第二个节点迭代。两个节点都应该正确打印数据。

现在我得到的行为可能是由于我在代码中犯了一个错误,或者可能是很明显的,因为我正在使用来自托管世界的指针。我需要你的帮助来解决这个问题

在c#中使用结构体实现链表时获取垃圾值(不安全结构)

使用本地Marshal.AllocHGlobal(类似于C中的malloc)分配内存

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Linked_List
{
public unsafe class NodeList
{
    public static Node * head ;
    public void AddNode(int d)
    {
        Node* newNode = (Node*)Marshal.AllocHGlobal(sizeof(Node)).ToPointer();
        newNode->data = d;
        newNode->link = null;
        Node* temp;
        if (head == null)
        {
            head = newNode;
        }
        else
        {
            temp = head;
            head = newNode;
            newNode->link = temp;
        }
        Console.WriteLine(head->data);
    }
    public void TraverseNodes()
    {
        Node* temp = head;
        while (temp != null)
        {
            Console.WriteLine(temp->data);
            temp = temp->link;
        }
    }
}
public unsafe struct Node
{
    public int data;
    public Node* link;
}

unsafe class  Program
{
    private  static void UnsafeDSImplementation()
    {
        var myLinkedList = new NodeList();
        myLinkedList.AddNode(2);
        myLinkedList.AddNode(4);
        myLinkedList.TraverseNodes();
    }

    static void Main(string[] args)
    {
        UnsafeDSImplementation();
    }
 }
}

注意:您还需要使用Marshal.FreeHGlobal

释放内存

看起来当head为null时,head和current都指向相同的内存位置。所以它们都变成了指向相同内存地址的指针。当第二个节点被添加时,你正在改变当前指针指向的值,但由于头部也指向相同的值,它也会随着当前的变化而变化。

我只是通过阅读你的代码才得出这个结论,所以我可能不正确。但这似乎就是原因。