Entwickler-Ecke
Basistechnologien - Pointer Anwendungsbeispiele
lapadula - Mo 17.07.17 15:10
Titel: Pointer Anwendungsbeispiele
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 - 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.
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); return 0; } |
Fragen? :D
Christian S. - Mo 17.07.17 20:23
lapadula hat folgendes geschrieben : |
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
Frühlingsrolle in seinem Projekt
frDriveNET [
https://www.entwickler-ecke.de/topic_frDriveNET_116499.html] einiges mit IntPtr anstellt.
Wenn Du wirklich an anderer Stelle Pointer verwenden möchtest, sähe ein Beispiel so aus:
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.
jfheins - Mo 17.07.17 21:29
lapadula hat folgendes geschrieben : |
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
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.
Delete - Mo 17.07.17 22:03
- Nachträglich durch die Entwickler-Ecke gelöscht -
C# - Di 18.07.17 09:04
Off-Topic
@jfheins
Hab es gerade mal getestet:
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(0, 1000)).ToArray();
var watch = Stopwatch.StartNew(); IterateChecked(array); watch.Stop(); checkedAccess.Add(watch.Elapsed);
array = Enumerable.Range(0, size).Select(j => rdm.Next(0, 1000)).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:
Quelltext
1: 2:
| Checked access for 10.000.000 elements: 166,493ms Unchecked access for 10.000.000 elements: 200,671ms |
Hier [
https://stackoverflow.com/questions/16713076/array-bounds-check-efficiency-in-net-4-and-above] gibt es auch eine gute Erklärung warum das so ist (die oberen beiden Antworten).
Off-Topic ende
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!