Autor Beitrag
lapadula
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 180
Erhaltene Danke: 10



BeitragVerfasst: Mo 17.07.17 15:10 
Hallo, habe mich ein wenig über Pointer informiert verstehe jetzt aber nicht genau wozu man diese in c# nutzen soll.

Man hat mehr Kontrolle indem ich selber auf die Speicheradressen zugreifen kann aber wie sieht das an einem Konkreten Beispiel aus.

Was nützt es mir zu wissen in welchen Speicherbereich sich meine Variable x befindet?

Mfg
hydemarie
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 475
Erhaltene Danke: 51



BeitragVerfasst: Mo 17.07.17 17:27 
Zu wissen, wo deine Variable sich befindet, ist nicht der einzige Vorteil.

Um Pointer zu verstehen, sollte man aufhören, in managed code wie C# zu denken. (Außerdem: Mitunter hast du es mit Hardware zu tun, auf der C# nicht läuft. Gerade auf embedded-Systemen kommst du mit C# oft nicht besonders weit.)

Denken wir mal etwas tiefer, denken wir in C.

Zitat:
wie sieht das an einem Konkreten Beispiel aus


Einfaches (weltfremdes, weil das so wahrscheinlich kaum jemand machen würde) Beispiel: Addition zweier Zahlen. Das kannst du entweder einfach oder effizient machen.

ausblenden C++-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
#include <stdio.h>

int addiere_ohne_pointer(int a, int b) {
    return a + b;
}

int addiere_mit_pointer(int* a, int* b) {
    return *a + *b;
}

int main(void) {
    int func1, func2;
    int val1 = 1, val2 = 1;

    func1 = addiere_ohne_pointer(val1, val2);
    func2 = addiere_mit_pointer(&val1, &val2);

    printf("%d / %d", func1, func2); /* sollte "2 / 2" ausgeben */
    
    return 0;
}


Fragen? :D

Für diesen Beitrag haben gedankt: lapadula
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 17.07.17 20:23 
user profile iconlapadula hat folgendes geschrieben Zum zitierten Posting springen:
Hallo, habe mich ein wenig über Pointer informiert verstehe jetzt aber nicht genau wozu man diese in c# nutzen soll.

Am Besten gar nicht ;-) Das Arbeiten mit Pointern ist einer managed Umgebung wie .NET ein notwendiges Übel, dem man aber so weit wie möglich aus dem Weg gehen sollte. Es widerspricht den grundlegenden Ideen von .NET, dass man eben nicht mehr direkt auf Speicherbereiche zugreift. Insbesondere wenn man auf Funktionen der Windows API zugreifen muss, kommt man nicht drum herum, ansonsten sind die Anwendungsszenarien aber begrenzt.

Man kann, wenn man mit Pointern und generell mit unsafe-Codebereichen arbeitet, vielleicht ein wenig Performance herausholen, aber meist steht der Gewinn an Performance auf der einen und der Verlust an Lesbarkeit und Sicherheit auf der anderen Seite in keinem sinnvollen Verhältnis. Wessen Anwendung es erfordert, auf diesem Level an der Performance zu schrauben, sollte eher erwägen, direkt auf native Sprachen wie C, C++ oder Delphi zu setzen.

Mit diesen Warnungen vorweg, kannst Du Dir ein Beispiel, welches in den Bereich der Windows-API-Aufrufe fällt bei uns in den Open-Source-Projekten angucken, wo user profile iconFrühlingsrolle in seinem Projekt frDriveNET einiges mit IntPtr anstellt.

Wenn Du wirklich an anderer Stelle Pointer verwenden möchtest, sähe ein Beispiel so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
            int[] allTheBestNumbers = new int[42];

            fixed(int* pAllTheBestNumbers = allTheBestNumbers)
            {
                pAllTheBestNumbers[44] = 42;
            }

            MessageBox.Show(allTheBestNumbers[44].ToString());

Wichtig: der Code funktioniert nur, wenn (a) das Projekt mit /unsafe compiliert wird und (b) die Methode, in der der Code steht, mit unsafe deklariert wird. Man sieht, dass die Hürden hier extra hoch gehängt werden.

Der Code zeigt, dass durch die Verwendung von Pointern z.B. die Range Checks von .NET ausgehebelt werden und ich auf das 45. Element eines 42 Elemente langen Arrays zugreifen kann. Das spart Zeit (weil keine Checks ausgeführt werden), kann aber auch dazu führen, dass man in Speicherbereiche schreibt, wo man nicht hin will.

Die Range Checks greifen dann wieder in der letzten Zeile, wo ich direkt auf das Array zugreife.

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".

Für diesen Beitrag haben gedankt: lapadula
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Mo 17.07.17 21:29 
user profile iconlapadula hat folgendes geschrieben Zum zitierten Posting springen:
Man hat mehr Kontrolle indem ich selber auf die Speicheradressen zugreifen kann aber wie sieht das an einem Konkreten Beispiel aus.

Was nützt es mir zu wissen in welchen Speicherbereich sich meine Variable x befindet?

Die Adresse nützt dir tatsächlich relativ wenig.

Was dir was bringen kann: Bei einem Arrayzugriff prüft C# immer den Index, ob er innerhalb der Grenzen ist. Wenn nicht, kommt die IndexOutOfRangeException.

Nun könnte es sein, dass du ein Array mit 1 Milliarde Elementen hast und alle mal 2 nehmen willst. (Nur ein Beispiel) Wenn du also
ausblenden C#-Quelltext
1:
2:
3:
4:
for (int i = max; i >= min; i--)
{
  arr[i] = arr[i] * 2;
}


schreibst, hast du für jede Multiplikation im Hintergrund zwei if-Abfragen. Wenn das problematisch ist, könntest du unsafe benutzen. Hier umgehst du diese Prüfungen, was im Endeffekt schneller sein kann (nicht muss).

Nur musst du dann viel mehr darauf achten, dass die Grenzen (hier: min und max) auch wirklich in jedem Fall stimmen, weil ein Problem nicht mehr direkt als Exception gemeldet wird.

Für diesen Beitrag haben gedankt: lapadula
Frühlingsrolle
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 17.07.17 22:03 
- Nachträglich durch die Entwickler-Ecke gelöscht -

Für diesen Beitrag haben gedankt: lapadula
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Di 18.07.17 09:04 
Off-Topic

@jfheins

Hab es gerade mal getestet:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
        static void IterateChecked(int[] array)
        {
            for (int i = 0; i < array.Length; i++)
                array[i] = (int) (3 * array[i] + 4 * Math.Sin(i));
        }

        static unsafe void IterateUnchecked(int[] array)
        {
            fixed (int* begin = &array[0])
            {
                int* end = begin + array.Length;
                for (int* it = begin; it < end; it++)
                    *it = (int) (3 * *it + 4 * Math.Sin(*it));
            }
        }

        static void Main(string[] args)
        {
            const int size = 10_000_000;
            const int loops = 20;

            var rdm = new Random();
            var uncheckedAccess = new List<TimeSpan>();
            var checkedAccess = new List<TimeSpan>();

            for (int i = 0; i < loops; i++)
            {
                var array = Enumerable.Range(0, size).Select(j => rdm.Next(01000)).ToArray();

                var watch = Stopwatch.StartNew();
                IterateChecked(array);
                watch.Stop();
                checkedAccess.Add(watch.Elapsed);

                array = Enumerable.Range(0, size).Select(j => rdm.Next(01000)).ToArray();

                watch.Restart();
                IterateUnchecked(array);
                watch.Stop();
                uncheckedAccess.Add(watch.Elapsed);
            }

            Console.WriteLine($"Checked access for {size:N0} elements:   {checkedAccess.Average(s => s.TotalMilliseconds):F3}ms");
            Console.WriteLine($"Unchecked access for {size:N0} elements: {uncheckedAccess.Average(s => s.TotalMilliseconds):F3}ms");

            Console.ReadKey();
        }


Bei mir kommt das raus:
ausblenden Quelltext
1:
2:
Checked access for 10.000.000 elements:   166,493ms
Unchecked access for 10.000.000 elements: 200,671ms


Hier gibt es auch eine gute Erklärung warum das so ist (die oberen beiden Antworten).

Off-Topic ende

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler

Für diesen Beitrag haben gedankt: lapadula