it-swarm.dev

Welche Programmierprobleme lassen sich am besten mit Zeigern lösen?

Nun, ich verstehe im Grunde, wie man Zeiger verwendet, aber nicht, wie man sie am besten verwendet, um besser zu programmieren.

Was sind gute Projekte oder Probleme, die durch die Verwendung von Zeigern gelöst werden müssen, damit ich sie besser verstehen kann?

58
dysoco

Beim Bearbeiten großer Datenmengen im Speicher leuchten Zeiger wirklich.

Das Übergeben eines großen Objekts als Referenz entspricht dem Weitergeben einer einfachen alten Zahl. Sie können die benötigten Teile direkt bearbeiten, anstatt ein Objekt zu kopieren, zu ändern und dann die Kopie zurückzugeben, die anstelle des Originals eingesetzt werden soll.

68
user7007

Mit dem Zeigerkonzept können Sie Daten nach Adresse referenzieren, ohne die Datenspeicherung zu duplizieren. Dieser Ansatz ermöglicht das Schreiben effizienter Algorithmen wie:

  1. Sortieren
    Wenn Sie Daten in einem Sortieralgorithmus verschieben, können Sie den Zeiger anstelle der Daten selbst verschieben. Denken Sie daran, Millionen von Zeilen in einer Zeichenfolge mit 100 Zeichen zu sortieren. Sie sparen viele unnötige Datenbewegungen.

  2. Verknüpfte Listen
    Sie können den nächsten und/oder vorherigen Artikelspeicherort speichern und nicht die gesamten Daten, die dem Datensatz zugeordnet sind.

  3. Parameter übergeben
    In diesem Fall übergeben Sie die Adresse der Daten anstelle der Daten selbst. Stellen Sie sich wieder einen Namenskomprimierungsalgorithmus vor, der in Millionen von Zeilen ausgeführt wird.

Das Konzept kann auf Datenstrukturen wie relationale Datenbanken erweitert werden, bei denen ein Zeiger einem Fremdschlüssel ähnelt. In einigen Sprachen wird die Verwendung von Zeigern wie C # und COBOL nicht empfohlen.

Beispiele finden sich an vielen Stellen wie:

Der folgende Beitrag kann in irgendeiner Weise relevant sein:

44
NoChance

Ich bin überrascht, dass keine andere Antwort dies erwähnt hat: Mit Zeigern können Sie nicht zusammenhängende und nicht lineare Datenstrukturen erstellen, bei denen ein Element auf komplexe Weise mit mehreren anderen verknüpft sein kann.

Verknüpfte Listen (einfach, doppelt und zirkulär verknüpft), Bäume (rot-schwarz, AVL, trie, binär, Raumpartitionierung…) und Diagramme sind Beispiele für Strukturen, die am natürlichsten anhand von Referenzen und nicht nur anhand von Werten erstellt werden können .

29
Jon Purdy

Ein einfacher Weg ist der Polymorphismus. Polymorphismus funktioniert nur mit Zeigern.

Außerdem verwenden Sie Zeiger immer dann, wenn Sie eine dynamische Speicherzuweisung benötigen. In C geschieht dies normalerweise, wenn Sie Daten in einem Array speichern müssen, aber die Größe beim Kompilieren nicht kennen. Sie würden dann malloc aufrufen, um den Speicher zuzuweisen, und einen Zeiger, um darauf zuzugreifen. Unabhängig davon, ob Sie es wissen oder nicht, verwenden Sie Zeiger, wenn Sie ein Array verwenden.

for(int i = 0; i < size; i++)
   std::cout << array[i];

ist das Äquivalent von

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

Mit diesem Wissen können Sie wirklich coole Dinge tun, z. B. ein ganzes Array in einer Zeile kopieren:

while( (*array1++ = *array2++) != '\0')

In c ++ verwenden Sie new, um Speicher für ein Objekt zuzuweisen und in einem Zeiger zu speichern. Sie tun dies immer dann, wenn Sie ein Objekt zur Laufzeit anstatt zur Kompilierungszeit erstellen müssen (d. H. Eine Methode erstellt ein neues Objekt und speichert es in einer Liste).

Um Zeiger besser zu verstehen:

  1. Finden Sie einige Projekte, die mit traditionellen C-Strings und Arrays herumspielen.
  2. Finden Sie einige Projekte, die Vererbung verwenden.

  3. Hier ist das Projekt, bei dem ich mir die Zähne geschnitten habe:

Lesen Sie zwei n x n Matrizen aus einer Datei ein, führen Sie die grundlegenden Vektorraumoperationen für sie aus und drucken Sie das Ergebnis auf den Bildschirm.

Dazu müssen Sie dynamische Arrays verwenden und durch Zeiger auf Ihre Arrays verweisen, da Sie zwei Arrays von Arrays haben (mehrdimensionale dynamische Arrays). Nachdem Sie das Projekt abgeschlossen haben, haben Sie eine ziemlich gute Vorstellung davon, wie Zeiger verwendet werden.

8
Jonathan Henson

Um wirklich zu verstehen, warum Zeiger wichtig sind, müssen Sie den Unterschied zwischen Heap-Zuordnung und Stapelzuweisung verstehen.

Das Folgende ist ein Beispiel für eine Stapelzuweisung:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

Auf dem Stapel zugewiesene Objekte sind nur für die Dauer der aktuellen Funktionsausführung vorhanden. Wenn der Aufruf von foo den Gültigkeitsbereich verlässt, wird auch die Variable f nicht mehr angezeigt.

Ein Fall, in dem dies zu einem Problem wird, ist, wenn Sie etwas anderes als einen integralen Typ von einer Funktion zurückgeben müssen (zum Beispiel die Foo-Struktur aus dem obigen Beispiel).

Zum Beispiel würde die folgende Funktion zu einem sogenannten "undefinierten Verhalten" führen.

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

Wenn Sie etwas wie struct Foo * Von einer Funktion zurückgeben möchten, benötigen Sie wirklich eine Heap-Zuordnung:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

Die Funktion malloc ordnet ein Objekt auf dem Heap zu und gibt einen Zeiger auf dieses Objekt zurück. Beachten Sie, dass der Begriff "Objekt" hier lose verwendet wird und eher "etwas" als Objekt im Sinne einer objektorientierten Programmierung bedeutet.

Die Lebensdauer von Heap-zugewiesenen Objekten wird vom Programmierer gesteuert. Der Speicher für dieses Objekt wird reserviert, bis der Programmierer es freigibt, d. H. Durch Aufrufen von free() oder bis das Programm beendet wird.

Bearbeiten: Ich habe nicht bemerkt, dass diese Frage als C++ - Frage markiert ist. Die C++ - Operatoren new und new[] Führen dieselbe Funktion aus wie malloc. Die Operatoren delete und delete[] Sind analog zu free. Während new und delete ausschließlich zum Zuweisen und Freigeben von C++ - Objekten verwendet werden sollten, ist die Verwendung von malloc und free im C++ - Code vollkommen legal.

8
Mike Steinert

Wenn Sie ein nicht triviales Projekt in C schreiben, müssen Sie herausfinden, wie/wann Zeiger verwendet werden sollen. In C++ verwenden Sie meistens RAII-fähige Objekte, die Zeiger intern verwalten, aber in C spielen Rohzeiger eine viel häufigere Rolle. Was für ein Projekt Sie machen sollten, kann alles sein, was nicht trivial ist:

  • Ein winziger und einfacher Webserver
  • Unix-Befehlszeilentools (weniger, Katze, Sortierung usw.)
  • Ein aktuelles Projekt, das Sie für sich selbst und nicht nur zum Lernen durchführen möchten

Ich empfehle den letzten.

4
Viktor Dahl

Fast alle Programmierprobleme, die mit Zeigern gelöst werden können, können mit anderen sichereren Typen von Referenzen (ohne Bezug auf C++ - Referenzen gelöst werden, aber das allgemeine CS-Konzept, eine Variable referenzieren zu lassen auf den Wert der an anderer Stelle gespeicherten Daten).

Zeiger als spezifische Implementierung von Referenzen auf niedriger Ebene, bei der Sie Speicheradressen direkt bearbeiten können, sind sie sehr leistungsfähig, können jedoch leicht gefährlich sein (z. B. auf Speicherstellen außerhalb des Programms zeigen).

Die direkte Verwendung von Zeigern hat den Vorteil, dass sie etwas schneller sind, da keine Sicherheitsüberprüfungen durchgeführt werden müssen. Sprachen wie Java, die Zeiger im C-Stil nicht direkt implementieren, leiden unter einem leichten Leistungseinbruch, reduzieren jedoch viele Arten von schwer zu debuggenden Situationen.

Warum Sie eine Indirektion benötigen, ist die Liste ziemlich lang, aber im Wesentlichen sind die beiden Schlüsselideen:

  1. Das Kopieren von Werten großer Objekte ist langsam und führt dazu, dass das Objekt zweimal in RAM) gespeichert wird (möglicherweise sehr kostspielig). Das Kopieren per Referenz erfolgt jedoch fast augenblicklich mit nur wenigen Bytes von RAM (für die Adresse) Angenommen, Sie haben ~ 1000 große Objekte (jedes entspricht etwa 1 MB RAM) im Speicher, und Ihr Benutzer muss in der Lage sein, das aktuelle Objekt auszuwählen (auf das reagiert wird) vom Benutzer). Mit einer Variablen selected_object, das eine Referenz auf eines der Objekte ist, ist viel effizienter als das Kopieren des Werts des aktuellen Objekts in eine neue Variable.
  2. Komplizierte Datenstrukturen, die auf andere Objekte wie verknüpfte Listen oder Bäume verweisen, wobei jedes Element in der Datenstruktur auf andere Elemente in der Datenstruktur verweist. Der Vorteil des Verweises auf andere Elemente in der Datenstruktur bedeutet, dass Sie nicht jedes Element in der Liste im Speicher verschieben müssen, nur weil Sie ein neues Element in die Mitte der Liste eingefügt haben (kann zeitlich konstante Einfügungen enthalten).
3
dr jimbob

Die Bildmanipulation auf Pixelebene ist mit Zeigern fast immer einfacher und schneller. Manchmal ist es nur mit Zeigern möglich.

2
Dave Nay

Zeiger sind ein wesentlicher Bestandteil jeder Datenstrukturimplementierung in C, und Datenstrukturen sind ein wesentlicher Bestandteil jedes nicht trivialen Programms.

Wenn Sie erfahren möchten, warum Zeiger so wichtig sind, sollten Sie lernen, was eine verknüpfte Liste ist, und versuchen, eine Liste ohne Verwendung von Zeigern zu schreiben. Ich habe Ihnen keine unmögliche Herausforderung gestellt (TIPP: Zeiger werden verwendet, um Speicherorte im Speicher zu referenzieren. Wie referenzieren Sie Dinge in Arrays?).

1
dan_waterworth

Zeiger werden in so vielen Programmiersprachen unter der Oberfläche verwendet, ohne den Benutzer zu nerven. Mit C/C++ können Sie nur darauf zugreifen.

Wann man sie verwendet: So oft wie möglich, da das Kopieren von Daten ineffizient ist. Wann Sie sie nicht verwenden sollten: Wenn Sie zwei Kopien wünschen, die einzeln geändert werden können. (Was im Grunde dazu führt, dass der Inhalt von object_1 an eine andere Stelle im Speicher kopiert und ein Zeiger zurückgegeben wird - diesmal zeigt er auf object_2)

1
mmlac

Beim Durchsehen der verschiedenen StackExchange-Sites wird mir klar, dass es eher in Mode ist, eine solche Frage zu stellen. Bei Gefahr von Kritik und Abstimmungen bin ich ehrlich. Dies ist nicht dazu gedacht, zu trollen oder zu flammen, ich möchte nur helfen, indem ich eine ehrliche Einschätzung der Frage gebe.

Und diese Einschätzung lautet wie folgt: Dies ist eine sehr seltsame Frage an einen C-Programmierer. So ziemlich alles, was es sagt, ist "Ich kenne C. nicht." Wenn ich etwas weiter und zynischer analysiere, gibt es einen Unterton einer Fortsetzung dieser "Frage": "Gibt es eine Abkürzung, mit der ich schnell und plötzlich das Wissen eines erfahrenen C-Programmierers erwerben kann, ohne Zeit zu investieren?" für ein unabhängiges Studium? " Jede "Antwort", die jemand geben kann, ist kein Ersatz dafür, das zugrunde liegende Konzept und seine Verwendung zu verstehen.

Ich finde es konstruktiver, C aus erster Hand gut zu lernen, als ins Internet zu gehen und die Leute danach zu fragen. Wenn Sie C gut kennen, werden Sie sich nicht die Mühe machen, Fragen wie diese zu stellen. Es ist so, als würden Sie fragen: "Welche Probleme lassen sich am besten mit einer Zahnbürste lösen?"

0
asveikau

Auch wenn Zeiger bei der Arbeit mit großen Speicherobjekten wirklich leuchten, gibt es dennoch eine Möglichkeit, dasselbe ohne sie zu tun.

Der Zeiger ist für die sogenannte dynamische Programmierung unbedingt erforderlich. Wenn Sie nicht wissen, wie viel Speicher Sie benötigen, bevor Ihr Programm ausgeführt wird. Bei der dynamischen Programmierung können Sie zur Laufzeit Speicherblöcke anfordern und Ihre eigenen Daten in diese einfügen. Sie benötigen also Zeiger oder Referenzen (der Unterschied ist hier nicht wichtig), um mit diesen Datenblöcken arbeiten zu können.

Solange Sie zur Laufzeit einen bestimmten Speicher beanspruchen und Ihre Daten in einem neu erfassten Speicher ablegen können, können Sie Folgendes tun:

  1. Sie können sich selbst erweiternde Datenstrukturen haben. Dies sind Strukturen, die sich erweitern können, indem sie zusätzlichen Speicher beanspruchen, solange ihre Kapazität aufgebraucht ist. Die Schlüsseleigenschaft jeder sich selbst erweiternden Struktur besteht darin, dass sie aus kleinen Speicherblöcken besteht (je nach Struktur als Knoten, Listenelemente usw. bezeichnet) und jeder Block Verweise auf andere Blöcke enthält. Diese "verknüpften" Strukturen bilden die meisten modernen Datentypen: Diagramme, Bäume, Listen usw.

  2. Sie können mit dem Paradigma OOP (Objektorientierte Programmierung)) programmieren. Das gesamte OOP basiert auf der Verwendung nicht direkter Variablen, sondern auf Verweisen auf Klasseninstanzen (als Objekte bezeichnet) ) und manipulieren sie. Keine einzelne Instanz kann ohne Zeiger existieren (obwohl es möglich ist, nur statische Klassen auch ohne Zeiger zu verwenden, ist dies eher eine Ausnahme).

0

Komisch, ich habe gerade eine Frage zu C++ beantwortet und über Zeiger gesprochen.

In der Kurzversion benötigen Sie NIEMALS Zeiger, es sei denn, 1) die von Ihnen verwendete Bibliothek zwingt Sie dazu, 2) Sie benötigen eine nullfähige Referenz.

Wenn Sie ein Array, eine Liste, einen String usw. benötigen, legen Sie es einfach auf den Stapel und verwenden Sie ein stl-Objekt. Das Zurückgeben oder Übergeben von stl-Objekten ist schnell (ungeprüfte Tatsache), da sie internen Code haben, der einen Zeiger anstelle eines Objekts kopiert und die Daten nur kopiert, wenn Sie darauf schreiben. Dies ist reguläres C++, nicht einmal das neue C++ 11, das es Bibliotheksschreibern leichter macht.

Ihre Frage könnte an dieser Stelle beantwortet werden

Wenn Sie einen Zeiger verwenden, stellen Sie sicher, dass er sich in einer dieser beiden Bedingungen befindet. 1) Sie übergeben Eingaben, die möglicherweise nullwertfähig sind. Ein Beispiel ist ein optionaler Dateiname. 2) Wenn Sie das Eigentum verschenken möchten. Wenn Sie den Zeiger übergeben oder zurückgeben, sind weder Kopien davon übrig, noch verwenden Sie den Zeiger, den Sie verschenken

ptr=blah; func(ptr); //never use ptr again for here on out.

Aber ich habe sehr lange keine Zeiger oder intelligenten Zeiger mehr verwendet und ich habe meine Anwendung profiliert. Es läuft sehr schnell.

ZUSÄTZLICHER HINWEIS: Ich stelle fest, dass ich meine eigenen Strukturen schreibe und sie weitergebe. Wie mache ich das ohne Zeiger? Es ist kein STL-Container, daher ist das Vorbeifahren an ref langsam. Ich lade immer meine Datenliste/Deques/Maps und so. Ich kann mich nicht erinnern, Objekte zurückgegeben zu haben, es sei denn, es war eine Art Liste/Karte. Nicht einmal eine Schnur. Ich habe mir Code für einzelne Objekte angesehen und festgestellt, dass ich so etwas mache { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }, also gebe ich so ziemlich Objekte zurück (mehrere) oder ordne einen Verweis vorab zu, um ihn zu füllen (einzeln).

Wenn Sie jetzt schreiben, dass Sie eine eigene Deque, Map usw. haben, müssen Sie Zeiger verwenden. Aber das musst du nicht. Lassen Sie STL und steigern Sie möglicherweise die Sorge darüber. Sie müssen nur Daten und die Lösungen schreiben. Keine Behälter, um sie zu halten;)

Ich hoffe, Sie verwenden jetzt nie Zeiger: D. Viel Glück beim Umgang mit Bibliotheken, die Sie dazu zwingen

0
user2528

Ich sehe Zeiger als meinen Zeigefinger, mit denen wir ein paar Dinge tun:

  1. wenn dich jemand nach etwas fragt, das du nicht tragen kannst, wie zum Beispiel, wo die Straße so und so ist, würde ich auf diese Straße "zeigen", und das tun wir im Falle von Argumenten, auf die Bezug genommen wird
  2. wenn wir etwas zählen oder durchlaufen, verwenden wir denselben Finger, und das tun wir in Arrays

bitte vergib diese schlechte Antwort

0
A.Rashad

Da Ihre Frage mit C++ gekennzeichnet ist, werde ich Ihre Frage für diese Sprache beantworten.

In C++ wird zwischen Zeigern und Referenzen unterschieden. Daher gibt es zwei Szenarien, in denen Zeiger (oder intelligente Zeiger) erforderlich sind, um bestimmte Verhaltensweisen zu erleichtern. Sie können unter anderen Umständen verwendet werden. Sie fragen jedoch, wie Sie sie am besten verwenden können, und unter allen anderen Umständen gibt es bessere Alternativen.

1. Polymorphismus

Mit einem Basisklassenzeiger können Sie eine virtuelle Methode aufrufen, die vom Objekttyp abhängt, auf den der Zeiger zeigt.

2. Persistente Objekte erstellen

Zeiger sind erforderlich, wenn ein Objekt dynamisch erstellt wird (auf dem Heap anstelle des Stapels). Dies ist erforderlich, wenn die Lebensdauer von Objekten länger sein soll als der Bereich, in dem sie erstellt wurden.

In Bezug auf "gute Projekte oder zu lösende Probleme", wie andere bereits hier gesagt haben, wird jedes nicht triviale Projekt Zeiger verwenden.

0
dangerousdave

Zeiger sind sehr nützlich für die Arbeit mit Geräten mit Speicherzuordnung. Sie können eine Struktur definieren, die beispielsweise ein Steuerregister widerspiegelt, diese dann der Adresse des tatsächlichen Steuerregisters im Speicher zuweisen und direkt bearbeiten. Sie können auch direkt auf einen Übertragungspuffer auf einer Karte oder einem Chip verweisen, wenn der MMU ihn dem Systemspeicher zugeordnet hat).

0
TMN

Als reales Beispiel das Erstellen eines Limit-Orderbuchs.

der ITCH 4.1-Feed hat beispielsweise das Konzept, Aufträge zu "ersetzen", bei denen sich die Preise (und damit die Priorität) ändern können. Sie möchten in der Lage sein, Bestellungen entgegenzunehmen und an einen anderen Ort zu verschieben. Das Implementieren einer doppelseitigen Warteschlange mit Zeigern macht den Vorgang sehr einfach.

0
Foo Bah