Über- und Unterläufe vermeiden

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Über- und Unterläufe vermeiden

Beitrag von Niki »

Howdy,

heute geht's mir mal um das Thema Über- und Unterläufe. Gegeben sei ein Pointer und ein vorzeichenbehafteter Offset. Wird ein Über- oder Unterlauf entstehen wenn ich den Offset auf den Pointer addiere?

Ich habe mir hierzu eine Template-Methode geschrieben. Hoffentlich ist die auch korrekt, denn obgleich des einfachen Codes fing das Hirn beim Testen an zu käsen.

Code: Alles auswählen

// Liefert "true", wenn pCurrent + offset den gültigen Adressbereich verlässt. Dabei gilt:
// 
// - Das Ende des gültigen Adressbereichs ist nicht bei 0xffffffff..., sondern noch davor,
//   so das ein Element vom Typ _TType noch platz hat.
// - pCurrent wird als gültig vorausgesetzt
template <class _TType>
bool overflows(const _TType * pCurrent, int offset)
{
    static const _TType * pLowerBound = nullptr;
    static const _TType * pUpperBound = (_TType *)(-1);

    if (offset > 0 && pUpperBound - offset < pCurrent)
    {
        return true;
    }
    else if (offset < 0 && pLowerBound - offset > pCurrent)
    {
        return true;
    }

    return false;
}
Ich frage mich ob und wie man das optimieren könnte. Das bedeutet nun nicht, dass ich bereits Performance-Problem habe. Wenn ich allerdings weiß, dass der Code keine wesentlichen Performance-Verluste verusacht, dann würde ich ihn zu Debug- und Sicherheitszwecken gerne an Stellen benutzen an denen ich es normalerweise vermeiden würde.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4855
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von Schrompf »

Der Standard sagt dazu, glaube ich, dass Overflow in Zeiger-Arithmetik undefiniert ist. Der Compiler kann mit dieser Logik also tun und lassen, was er will. Immerhin prüfst Du mit UpperBound minus Offset, das ist schonmal besser als den Overflow direkt zu prüfen.

Abeer wenn Du mit Overflow im Zeiger rechnest, solltest Du mal ernsthaft über Deine Programmlogik nachdenken. Immerhin prüfst Du damit ja nur gegen Overflow und nicht, ob der Zeiger tatsächlich auf gültigen Speicher verweist. Das wäre das eigentliche Problem, das ich lösen würde. Und wenn Du Zeigertests brauchst, um zu ermitteln, ob Du den Zeiger dereferenzieren darfst, ist meiner Meinung nach was grundsätzliches an Deinem Algorithmus faul.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Über- und Unterläufe vermeiden

Beitrag von Niki »

Schrompf hat geschrieben:Der Standard sagt dazu, glaube ich, dass Overflow in Zeiger-Arithmetik undefiniert ist. Der Compiler kann mit dieser Logik also tun und lassen, was er will. Immerhin prüfst Du mit UpperBound minus Offset, das ist schonmal besser als den Overflow direkt zu prüfen.
Logisch. Den Overflow "direkt" zu prüfen ist genau das was der Code vermeidet.
Schrompf hat geschrieben:Abeer wenn Du mit Overflow im Zeiger rechnest, solltest Du mal ernsthaft über Deine Programmlogik nachdenken. Immerhin prüfst Du damit ja nur gegen Overflow und nicht, ob der Zeiger tatsächlich auf gültigen Speicher verweist. Das wäre das eigentliche Problem, das ich lösen würde. Und wenn Du Zeigertests brauchst, um zu ermitteln, ob Du den Zeiger dereferenzieren darfst, ist meiner Meinung nach was grundsätzliches an Deinem Algorithmus faul.
Du tust grad so als wäre ich der einzige Mensch auf Erden der manchmal auf Overflow prüfen möchte :) Aber das liegt daran, dass du nicht alle Fakten hast. Es geht tatsächlich um Overflow, und nicht darum ob ich etwas dereferezieren darf oder nicht. Tatsächlich geht es mir auch nicht nur um Zeiger, aber die Diskussion auf Zeiger-Basis ist mir für diesen Thread ausreichend. Stell dir mal einen kleineren Typ vor, bei dem ein falscher Offset durch Wrap-Around schnell wieder im gültigen Bereich landet. Es geht um Sicherheitsmechanismen die mir beim Debuggen helfen, und zwar nicht nur meinen eigenen Code. Und diese Aussage bedeutet keineswegs, dass ich irgendwo bereits einen Fehler in der Programmlogik habe.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von dot »

Pointer Arithmetik liefert laut Standard rein prinzipiell schonmal nur dann ein definiertes Ergebnis, wenn du mit Pointern arbeitest, die auf ein Element in einem Array oder genau ein Element hinter das letzte Element eines Arrays zeigen und nur so lange das Ergebnis ebenfalls im selben Array liegt. Jeder andere Fall ist undefiniert und darf gerne auch deinen PC Feuer fangen lassen... ;)
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Über- und Unterläufe vermeiden

Beitrag von Niki »

dot hat geschrieben:Pointer Arithmetik liefert laut Standard rein prinzipiell schonmal nur dann ein definiertes Ergebnis, wenn du mit Pointern arbeitest, die auf ein Element in einem Array oder genau ein Element hinter das letzte Element eines Arrays zeigen und nur so lange das Ergebnis ebenfalls im selben Array liegt. Jeder andere Fall ist undefiniert und darf gerne auch deinen PC Feuer fangen lassen... ;)
O_O Ich muss wohl den Zug verpasst haben....

Nimm an ich habe ein Byte-Array der Länge 10 an Speicheradresse 500. Ich habe einen Pointer der auf Speicheradresse 505 zeigt. Willst du mir sagen, dass wenn ich 50 auf den Zeiger draufaddiere, dass ich dann plötzlich an Adresse 4711 landen kann, anstelle von 555?
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von Krishty »

seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von dot »

Niki hat geschrieben:Nimm an ich habe ein Byte-Array der Länge 10 an Speicheradresse 500. Ich habe einen Pointer der auf Speicheradresse 505 zeigt. Willst du mir sagen, dass wenn ich 50 auf den Zeiger draufaddiere, dass ich dann plötzlich an Adresse 4711 landen kann, anstelle von 555?
Wenn dein Byte-Array nur 10 Elemente hat und du da 50 draufaddierst, dann ist von Seiten des Standard überhaupt nichts garantiert. Ja, der Standard garantiert nichtmal, dass du Pointer, die nicht in ein Array zeigen, überhaupt ausrechnen kannst, ohne – bereits rein durch den Versuch des Ausrechnens – den Weltuntergang herbeizuführen. Du könntest natürlich an Adresse 555 landen. Genausogut könntest du an Adresse 123456789 landen. Genausogut könnte der PC Jingle Bells spielen und dir frohe Weihnachten wünschen...selbst mitten im Sommer...das Verhalten ist undefiniert. Jede Situation, in der ein Pointer auch nur rein theoretisch potentiell überlaufen könnte, ist sofort undefiniertes Verhalten... ;)
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Über- und Unterläufe vermeiden

Beitrag von Niki »

Ok, das war mir nicht bewusst. Aber ich denke mal da muss ich mich auch nicht schämen :D

Damit ist mein ganzer Overflow-Test natürlich für die Tonne. Ist jetzt aber nicht so schlimm, weil nur Debug-Hilfe.

Sorgen macht mir jedoch jetzt etwas anderes: Overlap-Tests, worum es ja auch in dem von Krishty geposteten Link geht. Gibt es eine Standard Methode die mir sagt, ob sich zwei Speicherbereiche überlappen? Wenn nicht, dann muss ich bei mir eine ganze Menge Code ändern, der dann anstelle von Overlap-Tests temporäre Buffer reserviert. Dadurch scheint jetzt kein direkter Nachteil zu entstehen, aber es ist halt viel Arbeit.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2369
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von Jonathan »

Ich verstehe immer noch nicht, wieso genau du soviel mit Zeigern hantieren musst und sei es auch nur zu Debuggzwecken. Wenn man die entsprechenden Container benutzt hat, haben diese auch Debugversionen die eigentlich derartige Dinge für dich erledigen sollten. Wenn man nicht gerade etwas sehr exotisches schreibt, sollte man Zeigerarithmetik eigentlich überhaupt nicht brauchen, bzw. vom Compiler erledigen lassen. Den der macht gewiss keine Flüchtigkeitsfehler.

Übrigens: Alle reden bei undefinierten Verhalten immer davon, der Computer würde explodieren, oder automatisch kündigen schreiben oder so einen Quatsch. Neulich hat aber jemand folgenden Artikel gepostet:
http://blog.llvm.org/2011/05/what-every ... -know.html
Das sollte man sich echt geben, den so übertrieben sind die ganzen Märchen gar nicht - es passieren tatsächlich Dinge, die man für vollkommen verrückt halten würde.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von dot »

Natürlich sind das keine Märchen, passiert andauernd, gerade gestern hatten wir hier ein imo schönes Beispiel: http://www.c-plusplus.de/forum/p2373266#2373266 ;)
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Über- und Unterläufe vermeiden

Beitrag von Niki »

Jonathan hat geschrieben:Ich verstehe immer noch nicht, wieso genau du soviel mit Zeigern hantieren musst und sei es auch nur zu Debuggzwecken. Wenn man die entsprechenden Container benutzt hat...
Ich nehme an mit "entsprechenden Containern" meinst du STL Container? Ich erweitere ein steinaltes Projekt; so alt, dass ich erstmal RDTSC rausschmeißen musste. Nix STL oder ähnliches. Alles selbst gebaut, von mir und ein paar anderen Programmierern. Glaub mir, da wird mit Zeigern hantiert, und wie... und ich will das Ding auch noch 64-bit kompatibel kriegen :D Da bin ich froh über jede Debughilfe.

Aber wenn es dich beruhigt, die Code-Basis die ich für neue Projekte benutze macht sehr viel Gebrauch von der STL. Das habe ich vor einem halben Jahr oder so alles auf STL umgestellt (hat Schrompf mich indirekt zu überredet, und das war gut so). Und seit ich nun neuerdings ein VS 2013 habe mache ich wieder Änderungen (UTF-Konvertierungen über STL, und einige der C++ 11 Features).

Was den Overlap-Test anbelangt, der war tatsächlich in der neuen Code-Basis. Deswegen war ich da auch beunruhigt. Ich habe aber in den sauren Apfel gebissen und das heute Nachmittag entfernt. Andere, vergleichbare Situationen habe ich in dem Code zum Glück nicht.

Und das Fazit dieses Threads... manche Leute mögen sich wundern, über Dinge die ich tue oder lasse, aber ich kann ja nicht jedes Mal einen ganzen Roman schreiben und dann erwarten das die Leute das auch lesen. Wichtig ist, dass dieser Thread, zumindest für mich, sehr positiv verlaufen ist. Hätte ich diese "Merkwürdigkeit" nicht gepostet, dann wüsste ich immer noch nicht, dass Zeiger solche Probleme machen können. Ich als Original Poster kann mich dafür also nur herzlichst bedanken.
Benutzeravatar
Krishty
Establishment
Beiträge: 8239
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Über- und Unterläufe vermeiden

Beitrag von Krishty »

Damit das hier nicht ZU ernst genommen wird: Man kann mit einem nicht standardkonformen Compiler durchaus solche Ueberlappungspruefungen implementieren. Ein Beispiel ist Visual C++, das ja einen Dialekt implementiert, der u.A. Win32-kompatibel ist. Da zu den Win32-Voraussetzungen ein flacher Adressraum gehoert, kann man sich unter Visual C++ darauf verlassen und berufen. Falls das Projekt also auf Windows limitiert ist, kann man ruhig so testen.

Aber mit GCC geht es nachweislich kaputt, und mit Clang hoechstwahrscheinlich auch, und darum gehoert zumindest ein #ifdef _MSC_VER drum.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Niki
Establishment
Beiträge: 309
Registriert: 01.01.2013, 21:52

Re: Über- und Unterläufe vermeiden

Beitrag von Niki »

Ja, Krishty

Bei dem steinalten Project benutze ich den Overflow Test in der ersten Post, gefolgt von einem Array-Bereichstest. Da zählen nur Windows und VC. Hauptsache es hilft, und das hat es auch schon, zumindest bei einem Crash-Bug. Aber im Notfall kann ich den Overflow Test hinterher immer noch wegwerfen.

Bei meiner neuen Code Basis ist es mir allerdings nicht egal. Glücklicherweise konnte ich den Overlap-Test aber ohne jegliche Nachteile komplett entfernen. Und es war weniger Arbeit als ich vermutet hatte. Falls es wen interessiert... es ging um eine Klasse, die einen dynamischen Speicherbereich verwaltet. Da gibt es beispielsweise die Möglichket den Content des Speicherbereichs mit anderem Content zu überschreiben, wobei allerdings der neue Content aus dem selben Speicherbereich wie der alte Content kommen kann. Der Overlap-Test diente dazu genau diesen Fall zu erkennen.
Antworten