10 Stimmen

Wie man Verweise auf Elemente von temporären Objekten erkennt

Mein Kollege hat kürzlich unser Programm in Windows kompiliert und einen Fehler der folgenden Art entdeckt:

  std::string a = "hello "; 
  std::string b = "world"; 
  const char *p = (a+b).c_str();
  printf("%s\n", p);

Der aus irgendeinem Grund in unseren Linux-Executable-Dateien nicht zum Absturz führte.

Keiner unserer Compiler gibt irgendeine Art von Warnung aus, daher machen wir uns nun Sorgen, dass dieser Fehler im Code existieren könnte.

Obwohl wir nach c_str()-Vorkommen suchen können und eine visuelle Inspektion durchführen können, besteht die Möglichkeit, dass jemand auch das Folgende getan hat:

struct I {
     int num;
     I()   { num=0; }
};

struct X { 
     I *m;
     X()  { m = new I; }
     ~X() { delete m; }
     I  get() { return *m; }   // Version 1 oder
     I& get() { return *m; }   // Version 2
};

und darauf zugegriffen hat wie:

  I& a = X().get();         // erhält einen Verweis auf eine temporäre Variable oder eine gültige Kopie?
  cout << a.num;

anstatt:

 cout << X().get().num;   

was sicher ist (nicht wahr?)

Frage: Gibt es eine Möglichkeit, solche Fehler zu erkennen (vielleicht mithilfe des Compilers oder sogar einer Behauptung)?
Ich muss sicher sein, dass, wenn der Autor von struct X get() zwischen Version 1 und 2 ändert, das Programm vor dem Fehler warnt

2voto

Einfache Antwort: Im Allgemeinen können Sie diese Fehler nicht abfangen, und der Grund dafür ist, dass es ähnliche Konstrukte gibt, die völlig in Ordnung sein könnten, also müsste der Compiler die Semantik aller Funktionen kennen, um Sie warnen zu können.

In einfacheren Fällen, wie z.B. beim Abrufen der Adresse einer temporären Variable, warnen Sie viele Compiler bereits, aber im Allgemeinen ist es ziemlich schwierig, wenn nicht sogar unmöglich für den Compiler zu wissen.

Für ein ähnliches Beispiel zum .c_str() betrachten Sie:

std::vector< const char * > v;
v.push_back( "Hi" );
const char* p = *v.begin();

Der Aufruf von begin gibt eine temporäre Variable zurück, ähnlich wie der Ausdruck (a+b), und Sie rufen eine Memberfunktion dieser temporären Variable auf (operator*), die einen const char* zurückgibt, was Ihrem ursprünglichen Fall ziemlich ähnlich ist (aus der Sicht der beteiligten Typen). Das Problem ist, dass in diesem Fall das Ziel noch gültig ist nach dem Aufruf, während in Ihrem Fall (.c_str()) es nicht der Fall ist, aber es ist Teil der Semantik der Operation, nicht der Syntax, die der Compiler für Sie überprüfen kann. Das gleiche gilt für das Beispiel .get(), der Compiler weiß nicht, ob die zurückgegebene Referenz auf ein Objekt zeigt, das nach dem Ausdruck gültig sein wird oder nicht.

All diese Fälle fallen unter die Kategorie des Undefinierten Verhaltens.

1voto

John Humphreys Punkte 35064

Schau dir die Lösung dieser Frage an, ich denke, sie macht etwas Ähnliches wie das, wonach du suchst:

C++ Fangen einer hängenden Referenz

Es gibt lösungen auf Laufzeitbasis, die den Code instrumentalisieren, um ungültige Zeigerzugriffe zu überprüfen. Ich habe bisher nur Mudflap verwendet (das in GCC seit Version 4.0 integriert ist). Mudflap versucht, jeden Zeiger (und jede Referenz) im Code zu verfolgen und überprüft jeden Zugriff, ob der Zeiger/die Referenz tatsächlich auf ein lebendes Objekt seines Basistyps zeigt. Hier ist ein Beispiel: {...}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X