Przypisywanie dziwnych wartości do wskaźnika na następny węzeł

Odpowiedz Nowy wątek
2020-06-29 18:06

Rejestracja: 3 lata temu

Ostatnio: 1 tydzień temu

0

Czołem. Bawię się teraz wskaźnikami w C# i za cholerę nie mogę pojąć, czemu instancja obiektu List przechowująca wskaźniki do poprzedniego i następnego węzła, po dodaniu wartości do wskaźnika węzła o nazwie Head, w mainie w instancji List wywala mi jakieś dziwne adresy w pamięci mimo że tam null pakuje. Może ktoś zna temat wskaźników w C# i pomoże :D Na kodzie łatwiej będzie zrozumieć:

namespace List
{
    unsafe public struct Node
    {
        public Node(int value, Node* prev = null, Node* next = null)
        {
            Value = value;
            PreviousNode = prev;
            NextNode = next;
        }

        public int Value;
        public Node* PreviousNode;
        public Node* NextNode;
    }

    unsafe public class List
    {
        public Node* Head;
        public Node* Tail;
        public int Count;

        public List()
        {
            Head = Tail = null;
        }

        unsafe public void Add(int value)
        {
            if (Head == null)
            {
                Node temp = new Node(value);
                Head = &temp;
            }
            else
            {
                if (Tail == null)
                {
                    *Tail = new Node(value, Head, null);
                    Head->NextNode = Tail;
                }
                else
                {
                    Node* temp = Tail;
                    *Tail = new Node(value, temp, null);
                }
            }
            Count++;
        }
    }
}
namespace ListApp
{
    class Program
    {
        unsafe static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            List.List list = new List.List();
            list.Add(1); //przy debugowaniu w klasie List tutaj jest ok, we wskaźniku jest obiekt z nullami do następnego i poprzedniego węzła
            Console.WriteLine(list.Count); /* w tym momencie list.Head->NextNode ma przypisane jakieś przypadkowe adresy w pamięci i każdy ten obiekt ma kolejny randomowy NextNode z pamięci i randomową wartość */
            Console.WriteLine(list.Head->Value); 
            Console.ReadKey();
        }
    }
}

Pozostało 580 znaków

2020-06-29 18:32

Rejestracja: 1 rok temu

Ostatnio: 1 godzina temu

0

Gdzies Ty polazł?
na cholerę CI unsafe wskaźniki w C#?

Pozostało 580 znaków

2020-06-29 18:34

Rejestracja: 3 lata temu

Ostatnio: 1 tydzień temu

0

Byłem na rozmowie o pracę jako programista C# i było pytanie o wskaźniki, więc widocznie ich używają. Także pomyślałem że dobrze będzie się dokształcić w tym temacie.

Pozostało 580 znaków

2020-06-29 18:40

Rejestracja: 1 rok temu

Ostatnio: 1 godzina temu

0
gruby907 napisał(a):

Byłem na rozmowie o pracę jako programista C# i było pytanie o wskaźniki, więc widocznie ich używają. Także pomyślałem że dobrze będzie się dokształcić w tym temacie.

To się kształć.
Nie zrozumiesz, dopóki nie zgłębisz istoty "unsafe" (danych niezarządzanych itd, itd, itd) - którego NIGDY nie używa się w kodzie aplikacyjnym

edytowany 1x, ostatnio: AnyKtokolwiek, 2020-06-29 18:42

Pozostało 580 znaków

2020-06-29 18:42

Rejestracja: 3 lata temu

Ostatnio: 1 tydzień temu

0

jak widać chyba ktoś jednak używa. Wiem co to znaczy unsafe i czym grozi używanie wskaźników. Pytam o czysto techniczną rzecz.

edytowany 1x, ostatnio: gruby907, 2020-06-29 18:42
Nie mam zamiaru Cie STRASZYĆ. Rozumiesz, na co wskazuje taki wskaźnik? - AnyKtokolwiek 2020-06-29 18:43
na adres komórki w pamięci RAM, to są chyba podstawy jakich uczą na każdej uczelni w Polsce. - gruby907 2020-06-29 18:46

Pozostało 580 znaków

2020-06-29 20:02

Rejestracja: 4 lata temu

Ostatnio: 30 sekund temu

0

ciekawe co na to @Wibowit który mówił że normalnie w C# się nie używa raw pointerów, a tylko w benchmarkach chcą nabić wydajności ;)

edytowany 2x, ostatnio: WeiXiao, 2020-06-29 20:03

Pozostało 580 znaków

2020-06-29 20:05

Rejestracja: 15 lat temu

Ostatnio: 35 sekund temu

0
WeiXiao napisał(a):

ciekawe co na to @Wibowit który mówił że normalnie w C# się nie używa raw pointerów, a tylko w benchmarkach chcą nabić wydajności ;)

To ktoś zamierza użyć kodu OPa w aplikacji biznesowej?

Poza tym, o ile dobrze rozumiem to ten kod jest totalnie popsuty. Autor wyciąga wskaźnik do obiektu zarządzanego, czyli takiego który jest przesuwany, kopiowany, usuwany, etc przez GC. To ma być dobre?


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit, 2020-06-29 20:07
nie, ale można się zastanawiać czy firma do której OP rekrutował czegoś tam nie robi w ten sposób :P - WeiXiao 2020-06-29 20:07
Miejmy nadzieję, że nie. Chociaż z drugiej strony - co mnie firmy piszące w C# obchodzą :p - Wibowit 2020-06-29 20:15

Pozostało 580 znaków

2020-06-29 20:15

Rejestracja: 13 lat temu

Ostatnio: 1 minuta temu

4

Node jest strukturą, więc jest alokowany na stosie (linia 32). Jak weźmiesz do tego wskaźnik i opuścisz funkcję, to tracisz ramkę stosu, potem następna funkcja nadpisuje ten obszar pamięci (czyli przy wołaniu Console.WriteLine nadpisujesz węzły).

Pozostało 580 znaków

2020-06-29 20:27

Rejestracja: 3 lata temu

Ostatnio: 1 tydzień temu

0
Afish napisał(a):

Node jest strukturą, więc jest alokowany na stosie (linia 32). Jak weźmiesz do tego wskaźnik i opuścisz funkcję, to tracisz ramkę stosu, potem następna funkcja nadpisuje ten obszar pamięci (czyli przy wołaniu Console.WriteLine nadpisujesz węzły).

kurde w C++ to było łatwiejsze :D to tak w skrócie: da się jakoś zrobić żeby wskaźników używać i żeby z poziomu Maina był cały czas ten sam obiekt?

edytowany 1x, ostatnio: gruby907, 2020-06-29 20:27

Pozostało 580 znaków

2020-06-29 20:31

Rejestracja: 1 rok temu

Ostatnio: 3 godziny temu

4

Ogólnie obiekty przesuwają się dowolnie w pamięci zarządzanej i ich adres może się zmieniać dynamicznie w dowolnym momencie, dlatego nie możesz po prostu przechowywać wskaźników w ten sposób. Potrzebujesz co najmniej słówka "fixed" i przypinania obiektów GCHandle.Alloc(myObject, GCHandleType.Pinned)

Prawdopodobnie firmie bardziej chodziło o wskaźniki IntPtr wykorzystywane w łączeniu kodu zarządzanego z niezarządzanym / PInvoke. Mimo że większość przykładów na Internecie operuje na IntPtr, to praktycznie wszędzie można je wymienić na SafeHandle które są automatycznie zwalniane przez GC - przykładowo we wszystkich wywołaniach WinApi można zwyczajnie zamienić IntPtr na SafeHandle i Marshaller się zajmie opakowaniem wskaźników i tym samym pozwoli uprościć kod o zwalnianie tych wskaźników (lub zwyczajne użycie ich w bloku using)

Używanie zwykłych wskaźników jest właściwie nigdzie niepraktykowane, niebezpieczne i nie przynosi wymiernych korzyści. Jeśli chodzi o performance to od C#7 masz dostępne klasy Span<t> i Memory<t> które w bezpieczny sposób mogą zastąpić operacje na wskaźnikach dla tablic i spokojnie dorównują im szybkością. Nie ma żadnego uzasadnienia dla używania pointerów w zarządzanym kodzie, chyba że prosisz się o problemy.


edytowany 3x, ostatnio: obscurity, 2020-06-29 20:34

Pozostało 580 znaków

2020-06-29 20:31

Rejestracja: 13 lat temu

Ostatnio: 1 minuta temu

1

Musisz go zaalokować na stercie (czyli nie może być strukturą, albo musisz go zboxować), potem wypadałoby go przypiąć (GCHandle.Alloc z opcją Pin) i wtedy dopiero brać wskaźnik. To znacząco ogranicza dostępność używanych typów, bo nie wszystko można pinować (ale to runtime krzyknie).

Możesz też alokować na LOH (zrób odpowiednio dużą tablicę i tam alokuj strukturę), ale od dotneta 4.5.1 (czy coś koło tego) można też kompaktować LOH-a, więc nie masz gwarancji, że dane nie zostaną przesunięte. Nie wiem, czy struktury można pinować, ale strzelałbym, że nie (szczególnie, że GCHandle pewnie zrobi boxing takowej przy wywołaniu Alloc).

Możesz też zaalokować pamięć natywną, taką poza GC, zaalokować strukturę tam (to pewnie wymagałoby ręcznej zabawy wskaźnikami lub TypedReferences lub Marshal) i wtedy brać wskaźniki.

Pozostało 580 znaków

Odpowiedz

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