DOs und Donts während der Verwendung von Zeigern

7

Es ist eine einfache, aber wichtige Frage. Was sind die Do's und Donts bei der Verwendung eines Zeigers in C und C ++, um sicherzustellen, dass SEGMENTATION FAULT auf AIX vermieden wird?

Wo char * wird vor Zeichenarray bevorzugt?

    
Sachin Chourasiya 12.11.2009, 13:21
quelle

10 Antworten

18

C ++ spezifisch

  • Vermeiden Sie die manuelle Speicherverwaltung . Verwenden Sie stattdessen RAII-Container wie std::auto_ptr , boost::scoped_ptr , boost::shared_ptr und die entsprechenden Array-Container. Noch einfacher, oft funktioniert std::vector einwandfrei und erfordert keinerlei Zeigerverwendung. Schließlich können Sie, anstatt Zeiger auf große oder veränderbare Datenstrukturen zu übergeben, Referenzen übergeben. Zeiger können Arrays darstellen, während Referenzen diese Mehrdeutigkeit vermeiden.
  • Das Indizieren von Arrays ist ein vernünftiger Ersatz für die meisten Zeigerarithmetik. Es ist im Allgemeinen nicht langsamer als die Zeigerarithmetik, die oft leichter zu korrigieren und schwerer zu lesen und zu warten ist. Im Allgemeinen sollten Sie nicht auf Kosten der Lesbarkeit mikrooptimieren.
  • Rufen Sie die richtige Deallokationsroutine auf: delete[] ist für Arrays reserviert, die nur über new ...[] , delete nur für Zeiger auf einzelne Objekte und free(...) nur für über C api zugewiesenen Speicher (z als malloc(..) ). Diese dürfen nicht verwechselt werden; C ++ - Deallokationsroutinen enthalten Destruktoraufrufe und müssen daher korrekt aufgerufen werden.
  • Initialisiere und setze sinnlose Zeiger auf NULL explizit. Es ist einfacher, eine zufällige Null-Dereferenzierung zu debuggen als einen inkorrekten Speicherzugriff. Es ist in Ordnung, den NULL -Zeiger aufzuheben, sodass Sie Ihren Destruktor nicht mit Überprüfungen überdecken müssen, um dies zu vermeiden. Wenn Sie delete vorzeitig auf ein Objekt setzen, sollten Sie den Zeiger auf NULL setzen, um zu vermeiden, dass derselbe Zeiger doppelt gelöscht wird und damit nicht versehentlich ein Dangling-Zeiger dereferenziert wird.
  • Wenn Sie die C ++ - Vererbung verwenden und den Destruktor überschreiben, lesen Sie in virtuellen Destruktoren nach; Diese werden für die Korrektheit benötigt (Kurz gesagt, die Basisklasse muss den Destruktor explizit als virtuell gekennzeichnet haben).
  • Beachten Sie, wer einen Zeiger besitzt , wenn Sie Speicher manuell verwalten müssen. Nur der Besitzer sollte das Objekt oder Array, auf das der Zeiger zeigt, nicht zuordnen, und kein anderes Objekt darf das Objekt verwenden, nachdem der Besitzer delete der Zeiger darauf ist. boost::shared_ptr ist ein problemloser, relativ niedriger Overhead-Container, der oft in Ordnung ist, wenn Sie einen Zeiger teilen müssen.
Eamon Nerbonne 12.11.2009 14:36
quelle
12
  1. Stellen Sie immer sicher, dass Ihre Zeiger initialisiert sind
  2. Immer die Länge des zugewiesenen Speicherbereichs kennen
  3. Überprüfen Sie immer auf NULL-Wert, wenn Sie mit einem Zeiger
  4. arbeiten
  5. Lösche die Zeiger immer nachdem du sie benutzt hast
  6. Überschreiben Sie niemals den Speicher hinter den Enden des Zeigers
  7. Vertraue niemals den Gültigkeitszeigern, die für deine Methoden bereitgestellt werden
  8. Niemals Benutzereingaben in einen Zeiger eingeben (zB bekommt)
  9. Mischen Sie niemals malloc / free mit new / delete
  10. Verteilen Sie niemals Zeiger auf Klassenobjekte mit malloc
  11. Niemals Klassenobjektzeiger mit freiem
  12. freigeben

Und zu guter Letzt

Verwenden Sie niemals Zeiger, es sei denn, Sie müssen ... Es gibt (const) Verweise, um die Konstruktion von Objekten zu vermeiden, die an Funktionen und STL-Container und Zeichenfolgen für die anderen Speicheranforderungen übergeben werden :: shared_ptr) Zeiger, wenn Sie wirklich Zeiger brauchen und diese nicht von Hand verfolgen wollen.

    
fritzone 12.11.2009 12:40
quelle
3

In C

  • Zeigen Sie nicht auf Speicher, den Sie nicht besitzen.
  • NULL-Zeiger nicht dereferenzieren.
  • Machen Sie Ressourcen frei, wenn Sie sie nicht brauchen.
  • Verlieren Sie nicht den Überblick über Ihre Zeiger (Vorsicht bei fehlgeschlagenen Realloks).
  • Missbrauche nicht void* .
  • Mischen Sie keine Zeigertypen: a char* ist nicht dasselbe wie double** .
  • Überprüfen Sie den Rückgabewert von Funktionen, die Zeiger zurückgeben.
pmg 12.11.2009 13:05
quelle
2

Schreiben Sie standardkonformen Code. Das sollte sich um die Plattform kümmern. Und viele andere Probleme. Und verwende const . Das sorgt für mehr Probleme.

    
dirkgently 12.11.2009 12:33
quelle
1

Verwenden Sie in C ++ immer virtuelle Destruktoren. Wenn Sie die Basisklasse löschen, wird sichergestellt, dass der destuctor der abgeleiteten Klasse aufgerufen wird. Wenn Sie dies nicht tun und die abgeleitete Klasse einige Zuweisungen vorgenommen hat, haben Sie keine Chance, sie zu bereinigen.

    
Dima 12.11.2009 13:11
quelle
1

HINWEIS: C spezifische Antwort- & gt;

Im Folgenden sind die häufigsten Ursachen für diese Segmentierungsverletzung oder Segmentierungsfehler aufgeführt:

Falsche Formatkontrollzeichenfolge in printf- oder scanf-Anweisungen: Stellen Sie sicher, dass die Formatsteuerungszeichenfolge die gleiche Anzahl von Konvertierungsspezifizierern (% 's) aufweist, wie printf oder scanf Argumente zum Drucken oder Lesen haben und dass die Spezifizierer dem Typ der zu druckenden oder zu lesenden Variablen entsprechen. Dies gilt auch für fprintf und fscanf.

Vergessen, "& amp;" zu verwenden zu den Argumenten für scanf: Die Funktion scanf nimmt als Argumente den Format-Kontrollstring und die Adressen der Variablen, in die er die eingelesenen Daten einfügt. Das "& amp;" Der (Adresse des) Operators wird verwendet, um die Adresse einer Variablen zu liefern. Es ist üblich zu vergessen, "& amp;" mit jeder Variable in einem Scanf-Aufruf. Das "& amp;" kann eine Segmentierungsverletzung verursachen.

Zugriff über die Grenzen eines Arrays hinaus: Stellen Sie sicher, dass Sie die Grenzen eines von Ihnen verwendeten Arrays nicht verletzt haben. h., Sie haben das Array nicht mit einem Wert subskribiert, der kleiner als der Index des niedrigsten Elements oder größer als der Index des höchsten Elements ist.

Fehler beim Initialisieren eines Zeigers vor dem Zugriff: Eine Zeigervariable muss einer gültigen Adresse zugewiesen werden (d. H. Auf der linken Seite einer Zuweisung erscheinen), bevor auf sie zugegriffen wird (d. H. Auf der rechten Seite einer Zuweisung erscheint). Stellen Sie sicher, dass Sie alle Zeiger initialisiert haben, um auf einen gültigen Speicherbereich zu zeigen. Die korrekte Zeigerinitialisierung kann auf verschiedene Arten erfolgen. Beispiele sind unten aufgeführt.

Falsche Verwendung des "& amp;" (Adresse von) und "" (Dereferenzierung) Operatoren: Stellen Sie sicher, dass Sie verstehen, wie diese Operatoren funktionieren. Wissen Sie, wann sie angewendet werden sollten und wann Sie sie nicht anwenden sollten. Wie oben erwähnt, ist es üblich zu vergessen, "& amp;" mit jeder Variable in einem Scanf-Aufruf. Denken Sie daran, scanf benötigt die Adresse der Variablen, die es einliest. Insbesondere wissen, wann "& amp;" und "" sind absolut notwendig und wenn es besser ist, sie zu vermeiden.

Fehlerbehebung für das Problem:

Überprüfen Sie JEDEN Platz in Ihrem Programm, der Zeiger verwendet, ein Array tiefstellt oder den Adressoperator (& amp;) und den Dereferenzierungsoperator (*) verwendet. Jeder ist ein Kandidat für die Ursache einer Segmentierungsverletzung. Stellen Sie sicher, dass Sie die Verwendung von Zeigern und der zugehörigen Operatoren verstehen. Wenn das Programm viele Zeiger verwendet und viele Vorkommen von & amp; und *, fügen Sie einige printf-Anweisungen hinzu, um den Ort anzugeben, an dem das Programm den Fehler verursacht, und untersuchen Sie die Zeiger und Variablen, die an dieser Anweisung beteiligt sind.

Denken Sie daran, dass printf-Anweisungen zu Debugging-Zwecken am Ende ihrer Formatsteuerungszeichenfolgen ein Zeilenvorschubzeichen (\ n) haben müssen, um das Löschen des Druckpuffers zu erzwingen.

    
wrapperm 12.11.2009 13:36
quelle
1

Ich habe vor einiger Zeit an einem Projekt teilgenommen, bei dem ein paar gute Entwickler nur nackte Zeiger benutzten. Obwohl sie über ausgezeichnete Fähigkeiten verfügen, verursachte ihr Programm (unter Windows / Linux / HP-UX) von Zeit zu Zeit Segfaults und es gab einige Speicherlecks.

Ich verstehe, dass, wenn ein Programm ziemlich groß ist und Threads hat, es sehr schwierig ist, alle Fehler zu finden, die mit Zeigern zusammenhängen, und wenn Sie unbedingt einen blossen Zeiger brauchen, sollten Sie Boost.Smart_ptr .

    
skwllsp 12.11.2009 14:58
quelle
0

Die Eigentumsfrage ist oft die schwierigste: Ihr Design sollte bereits darüber entscheiden. Dasjenige , das das Objekt besitzt, sollte dafür verantwortlich sein, es zu zerstören.

Aber ich habe Szenarios gesehen, in denen auch auf zerstörte Objekte Bezug genommen wurde - auch als "dangling pointers". Ihr Design sollte auch die "Gültigkeit" berücksichtigen. Jede Bezugnahme auf ein gelöschtes Objekt sollte deutlich ungültig sein. Wenn ich mich nicht irre, kann die weak_ptr -Klasse diese Entscheidung verschieben.

    
xtofl 12.11.2009 13:14
quelle
0

Scheitern Sie nicht beim Überprüfen der Grenzen.

    
Michael Foukarakis 12.11.2009 15:01
quelle
0

Nicht wirklich eine Antwort auf das "Wo Char * gegenüber Character-Array bevorzugt werden?" Frage, aber eine Sache, die vor kurzem jemanden gebissen, den ich überprüfte, war "sizeof".

Bedenken Sie Folgendes:

%Vor%

An vielen Stellen verwenden Sie "a" und "b" auf ähnliche Weise. Die Probleme beginnen, wenn Sie etwas tun wie:

%Vor%

Ich denke, die Moral der Geschichte ist: Sei vorsichtig mit sizeof (). Es ist wahrscheinlich besser, eine Größe konstant zu halten.

    
Mattias Nilsson 12.11.2009 15:11
quelle

Tags und Links