C# - unsafe mode

0

Cześć,

Chciałem prosić Was o jakąś wskazówkę dotyczącą problemu na który się natknąłem w czasie pisania programu na algorytmy (piszę o jaki przedmiot chodzi, gdyż zaraz pojawiłyby się pytania o to po co w ogóle zawracam sobie głowę unsafe contextem - zawracam, bo tak każą i już ; ) ). Sprawa wygląda następująco:

Mam typ struct:

 
public unsafe struct NODE
    {
        private int value;
        private NODE* next;

        public NODE(int value)
        {
            this.value = value;
            this.next = null;
        }

        public NODE* Next
        {
            get { return this.next; }
            set { this.next = value; }
        }

        public int Value
        {
            get { return this.value; }
            set { this.value = value; }
        }
    }

Listę Nodów:

public unsafe class MYLIST
    {
        public NODE* head;
        public MYLIST()
        {
            this.head = null;
        }

        public void Add(int valueToAdd)
        {
            NODE tmp = new NODE(valueToAdd);  // <=== *)
            if (this.head != null)
                tmp.Next = this.head;
            this.head = &tmp;
        }

        public void List()
        {
            NODE* p = this.head;
            if (this.head != null)
            {
                while (p != null)
                {
                    Console.Write("[{0}]", p->Value);
                    p = p->Next;
                }
            }
            else
                Console.WriteLine("[Lista pusta]");
        }
         
    }

oraz użycie tychże typów:

         // ...
            MYLIST ml = new MYLIST();
            ml.Add(1);
            ml.Add(1);

            ml.List();
         // ...

Problem polega na tym, że nie działa funkcja Add() klasy MYLIST. Dzieje się tak dlatego, że (jak sądzę) nie alokuję pamięci dla nowo wstawianego węzła (oznaczyłem to miejsce gwiazdką *) ). Nie wiem niestety jak to zrobić w C#. C oferował malloca, C# oferuje co prawda stackalloc, ale nie wiem jak w tej konkretnej sytuacji go wykorzystać i czu w ogóle jest to poprawny trop... Będę wdzięczny za wszelkie propozycje ; )

1

Nie wiem niestety jak to zrobić w C#. C oferował malloca, a C++ oferował operator new. Tak jak i C# :).
Nie jestem pewien czy ci pomogę, bo zawsze wychodziłem z założenia że C# nie jest do pisania niskopoziomowego.

Spojrzę za chwilę na ten kod i powiem czy coś się da zrobić, ale na razie coś co napisałem kilka dni temu dla innego forumowicza -
Da się i bez unsafe:

class SingleListNode<T>
{
    public SingleListNode() { }
    public SingleListNode(T value)
    {
        this.Value = value;
    }

    public T Value { get; set; }
    public SingleListNode<T> Next { get; set; }
}

class SingleList<T>
{
    SingleListNode<T> head;
    SingleListNode<T> tail;

    private void Initialise(T firstElement)
    {
        head = new SingleListNode<T>(firstElement);
        tail = head;
    }

    public void Add(T val)
    {
        if (head == null)
        {
            Initialise(val);
        }
        else
        {
            tail.Next = new SingleListNode<T>(val);
            tail = tail.Next;
        }
    }

    public T Get(int ndx)
    {
        SingleListNode<T> current = head;

        if (head == null)
            throw new System.IndexOutOfRangeException();

        for (int i = 0; i < ndx; i++)
        {
            current = current.Next;
            if (current == null)
                throw new System.IndexOutOfRangeException();
        }

        return current.Value;
    }
}

Ok, popatrzyłem na twój kod. Otóż błędu w nim za bardzo nie ma - w sumie jedynym błędem jest użycie C# ;). Chodzi o Garbage Collectora. W czasie kiedy program się uruchamia on zmienia adresy danych w pamięci i cała struktura wskaźnikowa może iść na łąkę się paść. Pozostaje tylko struktura referencyjna.

0
MSM napisał(a)

Chodzi o Garbage Collectora. W czasie kiedy program się uruchamia on zmienia adresy danych w pamięci i cała struktura wskaźnikowa może iść na łąkę się paść. Pozostaje tylko struktura referencyjna.

A oznaczenie wskaźników jako fixed, aby GC ich nie ruszał?

0

Cały program w fixed musiałby być.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Unsafe
{
    class Program
    {
        static void Main(string[] args)
        {
            List list = new List();
            list.Add(10);
            list.Add(15);
            list.Add(20);

            Console.WriteLine(list);
            Console.ReadKey(true);
        }

        public unsafe struct Node
        {
            public int Value { get; set; }
            public Node* Next { get; set; }
        }

        public unsafe class List
        {
            private Node* head;

            public List()
            {
                head = null;
            }

            ~List()
            {
                if (head == null) return;

                Node* element = head;
                while (element != null)
                {
                    Node* next = element->Next;
                    Marshal.FreeHGlobal(new IntPtr(element));

                    element = next;
                }

            }

            public void Add(int value)
            {
                Node* newNode = (Node*)Marshal.AllocHGlobal(sizeof(Node)).ToPointer();
                newNode->Value = value;
                newNode->Next = null;

                Node* tail = head;

                if (tail == null) head = newNode;
                else
                {
                    while (tail->Next != null)
                        tail = tail->Next;
                    tail->Next = newNode;
                }
            }

            public override string ToString()
            {
                if (head == null) return "Lista pusta";

                StringBuilder sb = new StringBuilder();
                
                Node* element = head;
                for (int i = 0; element != null; i++)
                {
                    sb.AppendFormat("Element nr {0}, wartość: {1}\n", i, element->Value);
                    element = element->Next;
                }

                return sb.ToString(0, sb.Length - 1);
            }
        }
    }
}
0

Dzięki, dokładnie o coś takiego mi chodziło.

0

Łolaboga! Przecież dokładnie od tego są typy referencyjne. Wystarczy żeby Node było klasą a nie strukturą

public class NODE
    {
        private int value;
        private NODE next;

        public NODE(int value)
        {
            this.value = value;
            this.next = null;
        }

        public NODE Next {get; set; }
        {
            get { return this.next; }
            set { this.next = value; }
        }

        public int Value
        {
            get { return this.value; }
            set { this.value = value; }
        }
    }

(swoją drogą te property można skrócić do public NODE Next { get; set; } i wywalić pola)

i… już! żadnych wskaźników, żadnych fixed, żadnych unsafe. Tak długo, jak NODE będzie class

 zamiast struct to będzie działać.

<code noframe>unsafe

jest dobre, potrzebne, ale nie do czegoś takiego.

0

Zią, wiemy. Ale całe zadanie polega na tym, by użyć wskaźników.

1 użytkowników online, w tym zalogowanych: 0, gości: 1