Suche nach 64-Bit-Fehlern in der Array-Implementierung

Suche nach 64-Bit-Fehlern in der Array-Implementierung

In PVS-Studio 3.43 haben wir die Art und Weise überarbeitet, wie der Viva64-Analyzer Fehler in den Klassen erkennt, die als Container (Arrays) dienen. Bisher haben wir uns an das Prinzip gehalten, dass, wenn eine Klasse über operator[] verfügt, ihr Parameter den Typ memsize (ptrdiff_t, size_t) haben muss und nicht int oder unsigned. Wir empfehlen dennoch, den memsize-Typ als Argument für operator[] zu verwenden. Es ermöglicht dem Compiler, in einigen Fällen einen effizienteren Code zu erstellen und einige 64-Bit-Fehler im Voraus zu vermeiden. Jetzt haben wir den Ansatz dahingehend geändert, mit Klassen zu arbeiten, die über operator[] verfügen, wodurch wir die Anzahl unnötiger Diagnosewarnungen reduzieren können.

Betrachten wir ein Beispiel, das einen Fehler enthalten könnte, wenn wir mit großen Datenmengen arbeiten wollen:

class MyArray {
  std::vector <float> m_arr;
  ...
  float &operator[](int i)
  {
    return m_arr[i];
  }
} A;
...
int x = 2000;
int y = 2000;
int z = 2000;
A[x * y * z] = 33;

Der erste Nachteil dieses Codes ist dieser operator[] erlaubt uns nicht, auf das Element mit einer Nummer über INT_MAX zuzugreifen .

Notiz. Eine wichtige Sache möchte ich klarstellen. In der Release-Version kann der Compiler für einen Code wie den im Beispiel eine Optimierung bereitstellen, die funktioniert, da das 64-Bit-Register verwendet wird, um den Index zu berechnen und zu übergeben. Ich werde einen separaten Beitrag erstellen, um dieses Beispiel genauer zu untersuchen. Aber dieses Glück macht den Code nicht richtig. Hier erfahren Sie mehr über gefährliche Optimierungen.

Der zweite Nachteil des Codes liegt im Ausdruck x*y*z wo ein Überlauf auftreten kann, wenn mit einem großen Array gearbeitet wird.

Zuvor hat der Analysator zwei Warnungen (V108) generiert. Die erste ist die Verwendung von int Typ beim Aufruf des Arrays m_arr . Die zweite verwendet int Typ beim Aufruf des Arrays A. Obwohl operator[] der Klasse MyArray nimmt ein int Argument haben wir angeboten, einen memsize-Typ als Index zu verwenden. Wenn der Programmierer die Typen der Variablen x geändert hat , y und z zu ptrdiff_t , hat der Visual C++-Compiler eine Warnung zur Typkonvertierung in der Zeile A[x * y * z] =33 gestartet :

Warnung C4244:'Argument' :Konvertierung von 'ptrdiff_t' nach 'int', möglicher Datenverlust

Diese Warnung forderte den Benutzer auf, das Argument in operator[] zu ändern und der Code wurde absolut korrekt. Hier ist ein Beispiel für den korrigierten Code:

class MyArray {
  std::vector <float> m_arr;
  ...
  float &operator[](ptrdiff_t i)
  {
    return m_arr[i];
  }
} A;
...
ptrdiff_t x = 2000;
ptrdiff_t y = 2000;
ptrdiff_t z = 2000;
A[x * y * z] = 33;

Leider hat dieser Diagnoseansatz einen großen Nachteil. In einigen Fällen operator[] kann nicht geändert oder mit int verwendet werden da der Index absolut gerechtfertigt ist. Und es schien, dass der Viva64-Analyzer viele unnötige Warnungen generierte. CString Klasse von MFC kann als Beispiel dienen. Der Operator in CString Klasse hat den Prototyp:

TCHAR operator []( int nIndex ) const;

Aus diesem Grund wird der Code als gefährlich diagnostiziert:

int i = x;
CString s = y;
TCHAR c = s[i];

CString Klasse kann nicht bearbeitet werden. Und, nun ja, kaum jemand wird CString verwenden Geben Sie ein Standardprogramm ein, um mit Zeilen zu arbeiten, die länger als zwei Milliarden Zeichen sind. Der Viva64-Analysator wiederum generierte viele Warnungen zu diesem Code. Wenn der Programmierer den Indextyp von int geändert hat zu ptrdiff_t , war es der Compiler, der die Warnungen generiert hat. Wir könnten die Warnungsunterdrückung //-V108 verwenden, aber das würde den Code überladen. Mehr zur Warnungsunterdrückung erfahren Sie im Artikel:PVS-Studio:Verwendung der Funktion "Als Fehlalarm markieren".

Wir haben uns entschieden, das Konstrukt A[x * y * z] =33; zu berücksichtigen aus dem ersten Beispiel sicher. Wenn nun operator[] akzeptiert einen 32-Bit-Typ als Argument (zum Beispiel int ) und wir diesen Operator auch mit einem 32-Bit-Typ aufrufen, gilt dieser Aufruf als sicher.

Natürlich kann es einen Fehler verbergen. Aus diesem Grund haben wir eine neue Diagnosewarnung V302 hinzugefügt:"Member operator[] of 'FOO' class has an 32-bit type argument. Use memsize-type here". Diese Diagnosewarnung wird für operator[] generiert mit einem 32-Bit-Argument definiert.

Die Intelligenz dieser Lösung besteht darin, dass diese Warnung nicht für den Bibliothekscode generiert wird, der nicht für Änderungen zugänglich ist. D.h. für die Klasse CString wird keine V302-Warnung generiert aber für die Benutzerklasse MyArray .

Wenn Operator[] in MeinArray class ist richtig und sollte eigentlich den Typ int haben , muss der Programmierer nur eine Warnungsunterdrückung //-V302 in diese Klasse schreiben, anstatt an mehreren Stellen, an denen sie verwendet wird.

Die letzte Änderung im Zusammenhang mit der Array-Verarbeitung betrifft die Einführung einer weiteren Warnung V120:"Member operator[] of object 'FOO' deklared with 32-bit type argument, but called with memsize type argument". Insgesamt kopiert diese Warnung die Compiler-Warnung zum Konvertieren eines 64-Bit-Typs in einen 32-Bit-Typ. Dies ist nützlich, wenn der Compiler viele Warnungen generiert und Sie unter anderem die Informationen zur Codeeffizienz auf einem 64-Bit-System vermissen.