push_back maps mit long double mit Runtime-Error



  • Hallo zusammen,

    ich hab gerade ein Problem das ich nach mehreren Stunden nicht beurteilen kann. In meinem Projekt habe ich die double Werte nun durch long double ersetzt (nur als ein Test). Da ich mit "using" arbeit ist da auch recht einfach mit der Änderung einer einzigen Zeile erledigt.

    using scalar = long double;
    

    Allerdings bekomme ich ein Run-Time Error der mich derzeit in der Luft hängen lässt. Folgende Zeile macht beim zweiten mal wenn diese Durchlaufen wird Probleme:

    ENHANCED_.push_back(map<word, scalar>());
    

    Wobei die Klassenvariable vom Typ

    mapList<word, scalar> ENHANCED_;
    

    ist. Um die korrekten Typen zu schreiben, hier nun ausführlich:

    using scalar = long double;
    using word = std::string;
    template<class T, class Z> using mapList = typename std::vector<std::map<T, Z> >;
    

    damit folgt:

    std::vector<std::map<std::string, long double> > ENHANCED_;
    

    Um den Fehler einzuschränken hab ich zu den allgemeinen Debug-Infos noch weitere hinzugefügt, vor allem vor und hinter der Problemzeile.

    std::cout<< "Before push_back" << std::endl;
    ENHANCED_.push_back(map<word, scalar>());
    std::cout<< "After push_back" << std::endl;
    

    Die Ausgabe ist nun wie folgt (mit zusätzlichem berzeits im Code integriertem Debug-Zeug):

    Before push_back
      After push_back
    
     --> AFC::ChemistryReader::analyzeReaction
     --> AFC::StringManipulator::splitStrAtDelimiter
    String to split: H+O2=OH+O
    Delimiter is: =
     --> AFC::ChemistryReader::analyzeReacSite
     To be analyzed: r
     --> AFC::StringManipulator::removeAtEnd
     --> AFC::StringManipulator::removeAtEnd
     --> AFC::ChemistryReader::analyzeReacSite
     To be analyzed: p
     --> AFC::StringManipulator::removeAtEnd
     --> AFC::StringManipulator::removeAtEnd
    
      Before push_back
    *** Error in `/home/shorty/OpenFOAM/development/flameletcreator/src/automaticFlameletCreator': free(): invalid pointer: 0x000000000097a150 ***
    Aborted (core dumped)
    

    Was erkennbar ist, ist das die problematische Codezeile, beim ersten mal ohne Probleme durchläuft und dann beim 2ten mal nicht mehr und der invalid pointer Fehler auftritt. Das ich meinen Container ENHANCED_ falsch erweitere bezweifel ich da ein Test mit einem Testcontainer und einer Schleife über 100x ohne Probleme läuft (außerdem gibt es bei der Verwendung mit einfachen double oder float Zahlen keine Probleme):

    std::vector<std::map<string, long double> > Test;
    
    for (int i=0; i<100; i++)
    {
        Test.push_back(map<std::string, long double>());
    }
    std::cout<< Test.size() << std::endl;
    

    Bezüglich dem free() invalid pointer kann ich nur folgende Info geben,
    Ich arbeite in meinem Code mit keinen Pointer, reserviere Speicher mit new oder gebe Speicher frei. Sobald ich den Typ long double wieder auf double setze, ist alles in Ordnung; nur hab ich das Gefühl das durch diesen Fehler irgendwo was im Argen ist. Kanns aber selber nicht beurteilen oder den Fehler finden 😕 Müsste ja dann irgendwie im Container ausgelöst werden, da hier ja mit Pointern gearbeitet wird.

    Ich hoff es ist verständlich und hab genügend Infos oder Testcode bereitgestellt. Sollte was Fehlen liefer ich gern was nach.

    Viele Grüße
    Tobi


  • Mod

    Klingt tendenziell danach, als würdest du irgendwo über die Grenze eines Feldes hinaus schreiben. Du bist auf einem Linuxsystem mit GCC unterwegs? Du hast mehrere Optionen, mit denen du den Fehler schnell finden solltest:

    • Du schaltest den Debugmodus der STL an. Dazu übersetzt du das Programm mit dem Schalter -D_GLIBCXX_DEBUG und lässt es einfach laufen.
    • Du benutzt valgrind, das ist ein Zusatzprogramm, das auf vielen Linuxsystemen bereits installiert ist, falls nicht, holst du es dir. valgrind kann sehr viel. Es könnte schon reichen, dein Programm einfach mit den Standardeinstellungen zu starten ( valgrind name_deines_programms ). Ich nutze bei ersten explorativen Analysen immer
    valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes name_des_programms
    

    In beiden Fällen empfiehlt es sich, bei der Übersetzung Debugsymbole erzeugen zu lassen (Schalter -g ), dann können eventuelle Fehlermeldungen besser mit der verursachenden Codezeile in Verbindung gebracht werden.



  • Hallo Sepp und wieder vielen Dank. Du bist stets aktiv, sehr lobenswert und das schätz ich auch sehr. Du liegst richtig, dass ich auf einem Linux-System arbeite und danke für den Compiler-Flag (für das Debuggen der STL). Den Schalter -g hab ich bereits beim Kompilieren drin. Nach rekompilierung erhalte ich (wie du bereits sagst), out-of-bounds. Das werde ich morgen Abend genauer prüfen. Auch danke für das Programm das du erwähnt hast. Werde ich mir auch im Hinterkopf behalten. Allerdings verstehe ich nicht, wieso der Fehler nur mit long double und nicht mit double auftritt. Den Prinzipiell sollte das Problem ja auch mit double auftreten (sofern ich über ein Feld hinaus schreibe). Wenn ich dich richtig verstehe und auch den Fehler, dann versuche schreib ich in ein Container der 3 Felder hat in das 4te (zumindest laut Ausgab) zu schreiben. Das ist aber dann ja nicht bedingt durch double oder long double sonder wäre ja ein Fehler in meiner Programmierung (falsch indiziert oder ein Feld falsch erweitert). Anbei noch die Ausgabe meines Programms nach Rekompilierung (nun erfolgt der Fehler auch direkt beim ersten Aufruf und - für mich wichtig - ich seh das es nicht in der von mir zuvor gedachten Stelle ist). Morgen wird da weiter analysiert. Danke dir Sepp.

    /usr/include/c++/4.8/debug/vector:346:error: attempt to subscript container 
        with out-of-bounds index 4, but container only holds 4 elements.
    
    Objects involved in the operation:
    sequence "this" @ 0x0x8fe940 {
      type = NSt7__debug6vectorIeSaIeEEE;
    }
    

  • Mod

    Das kann man nur beantworten, wenn man auch deinen Code sieht. Kommt in deinem Code zufällig irgendwo sizeof vor?



  • Hallo Sepp,

    in meinem Code kommt keine sizeof vor. Höchstens STL.size()-1. Ich werde heute Abend erstmal die Stelle rausfinden, in der sich der Fehler befindet und danach werde ich mich erneut hier melden.



  • Shor-ty schrieb:

    Hallo Sepp,

    in meinem Code kommt keine sizeof vor. Höchstens STL.size()-1. Ich werde heute Abend erstmal die Stelle rausfinden, in der sich der Fehler befindet und danach werde ich mich erneut hier melden.

    sry, wenns ne blöde frage ist, vll hab ich i.was nicht richtig verstanden oder gelesen aber:

    wieso "stl.size() - 1" ?

    wenn du einen std::vector.size() benutzt, brauchst du kein -1 ! (ist sowie so unschön finde ich...) 🙄

    wozu machst du "-1"?

    http://www.cplusplus.com/reference/vector/vector/size/
    https://technet.microsoft.com/de-de/library/security/bb385259

    ich weiß nicht genau aber kann das nicht sogar zu fehlern führen...?
    oder liege ich falsch?

    weil wenn du das machst und nur 1 element im vetor ist dann versuchst du, den vector doch auf das element -1 zuschreiben oder nicht?

    --> also vector[-1] zu schreiben...
    (oder verhinderst du so etwas oder kann das gar nicht vorkommen bei deinem programm?) 😮

    lg



  • Das Problem dürfte eher bei leeren Vectoren auftreten:

    vector<int> v;
    cout << v.size() - 1;
    

    Ergibt einen sehr großen Wert, den man wohl nicht haben wollte... Bei 1-elementigen Vectoren kommt erwartungsgemäß 1-1=0 heraus. Sehe nicht, wie du da auf v[-1] kommen willst.



  • KeineAhungAber? schrieb:

    sry, wenns ne blöde frage ist, vll hab ich i.was nicht richtig verstanden oder gelesen aber:

    wieso "stl.size() - 1" ?

    wenn du einen std::vector.size() benutzt, brauchst du kein -1 ! (ist sowie so unschön finde ich...) 🙄

    wozu machst du "-1"?

    http://www.cplusplus.com/reference/vector/vector/size/
    https://technet.microsoft.com/de-de/library/security/bb385259

    ich weiß nicht genau aber kann das nicht sogar zu fehlern führen...?
    oder liege ich falsch?

    weil wenn du das machst und nur 1 element im vetor ist dann versuchst du, den vector doch auf das element -1 zuschreiben oder nicht?

    --> also vector[-1] zu schreiben...
    (oder verhinderst du so etwas oder kann das gar nicht vorkommen bei deinem programm?) 😮

    lg

    Für std::vector< mag das stimmen, aber std::string bietet zumindest vor C++11 kein back() . Und wenn man vorher prüft, ob ein Container nicht leer ist kann man durchaus sowas schreiben. Und wenn min. 1 Element im Container vorhanden ist greift man mit size() -1 immer auf ein gültiges Element zu, nämlich dem letzten.



  • wob schrieb:

    Das Problem dürfte eher bei leeren Vectoren auftreten:

    vector<int> v;
    cout << v.size() - 1;
    

    Ergibt einen sehr großen Wert, den man wohl nicht haben wollte... Bei 1-elementigen Vectoren kommt erwartungsgemäß 1-1=0 heraus. Sehe nicht, wie du da auf v[-1] kommen willst.

    ja mein fehler ...
    ich meinte leere vectoren, entschuldigung war vorhin etwas unkonzentriert.

    welchen "sehr großen wert" oder wie meinst du das? (zur ausgabe des letzten elementes ist schon klar aber dazu kann ich z.B. auch end() - 1 benutzen...)

    meines wissens nach kommt bei deinem beispiel 0 - 1 heraus --> was dann vector[-1] wäre...

    wo wäre hier der sinn? (oder der sehr große wert?)
    vor einem size()-1 sollte aufjedenfall klar sein das der vector aufkeinenfall leer ist...

    DocShoe schrieb:

    KeineAhungAber? schrieb:

    sry, wenns ne blöde frage ist, vll hab ich i.was nicht richtig verstanden oder gelesen aber:

    wieso "stl.size() - 1" ?

    wenn du einen std::vector.size() benutzt, brauchst du kein -1 ! (ist sowie so unschön finde ich...) 🙄

    wozu machst du "-1"?

    http://www.cplusplus.com/reference/vector/vector/size/
    https://technet.microsoft.com/de-de/library/security/bb385259

    ich weiß nicht genau aber kann das nicht sogar zu fehlern führen...?
    oder liege ich falsch?

    weil wenn du das machst und nur 1 element im vetor ist dann versuchst du, den vector doch auf das element -1 zuschreiben oder nicht?

    --> also vector[-1] zu schreiben...
    (oder verhinderst du so etwas oder kann das gar nicht vorkommen bei deinem programm?) 😮

    lg

    Für std::vector< mag das stimmen, aber std::string bietet zumindest vor C++11 kein back() . Und wenn man vorher prüft, ob ein Container nicht leer ist kann man durchaus sowas schreiben. Und wenn min. 1 Element im Container vorhanden ist greift man mit size() -1 immer auf ein gültiges Element zu, nämlich dem letzten.

    stimmt
    wenn er vorher prüft und es zum zugriff auf das letzte element verwendet macht es sinn ok...
    deshalb hatte ich ja gefragt wozu er es benutzt... 🙄



  • hatte ich eben vergessen, sry

    ich wollte eig auch auf "vector::back()" hinaus der direkt das letzte element
    zurückgibt...
    (jedoch sollte auch hier klar sein das der container nicht leer ist...)



  • KeineAhungAber? schrieb:

    meines wissens nach kommt bei deinem beispiel 0 - 1 heraus --> was dann vector[-1] wäre...

    wo wäre hier der sinn? (oder der sehr große wert?)

    Nein, es kommt nicht 0 - 1 = -1 heraus, sondern (size_t)0 - 1 = was_grosses. Und size_t ist vorzeichenlos und da gibt es kein -1, sondern du hast einen Überlauf auf die größte Zahl je nach Größe von size_t, zum Beispiel auf 18446744073709551615 bei 64 bit.



  • Hallo zusammen,

    die Lösung bzw. das Problem ist sehr einfach und konnte durch den Hinweis von Sepp schnell behoben werden. Im Code habe ich ein Container der Größe i wobei jedes Feld des Containers wieder ein Container mit 4 Felder hat; nachfolgend der Code wobei die kommentierten Zeilen das Original widerspiegeln:

    //List<scalarField> TROEcoeffs;  <--- Private class variable
    std::vector<std::vector<long double> > TROEcoeffs;
    

    TROE ist eine Art modifizierter Algorithmus, der 4 Parameter hat, wobei einer oft nicht verwendet wird (Feld #4). Ich prüfe das und setzen den Parameter #4 gleich auf Null, damit ich keine Probleme erhalte und das Feld initialisiert wird. Allerdings hab ich dann folgenden Fehler gemacht:

    //TROEcoeffs.push_back(scalarField(3));
    //TROEcoeffs_[nReac_][4] = 0.;
    
    TROEcoeffs.push_back(std::vector<long double>(4));
    TROEcoeffs[TROEcoeffs.size()-1][4} = 0.;
    

    nReac_ ist ein Integer der die aktuelle Reaktion darstellt und von 0 - n läuft. Somit die Codezeilen in identisch. Der Fehler ist nun simple, das letzte Feld ist eben [3] und nicht [4]. Daher auch der Fehler. Was mich jedoch jetzt noch interessieren würde, wäre, wieso das nur mit long double und nicht mit double auftritt. Eigentlich sollte / müsste der Fehler ja in beiden Varianten auftreten.

    Bezüglich der Diskussion mit STL.size()-1.

    Ich verwende das nur in Kombination mit nicht leeren Containern bspw.

    // STEP 3: insert arrhenius coeffs
        data.arrheniusCoeffs
        (
            stod(tmp[tmp.size()-3]),
            stod(tmp[tmp.size()-2]),
            stod(tmp[tmp.size()-1])
        );
    

    da ich keine bessere Lösung gefunden hatte. Das hab ich allerdings nur in meiner Reader-Classen. Die STL's sind aber nicht leer, das wird vorher geprüft.

    @Sepp. Danke für die Nennung des Flags. Hat wunderbar funktioniert.

    Grüße Tobi


  • Mod

    Ein paar Spekulationen, weshalb das beobachtete Verhalten hier anders sein könnte:
    Grundannahme ist, dass long double bei dir größer ist als double. Das kann so sein, muss aber nicht. Falls dem so ist:
    1. Dann ist "ein Element zu weit" beim long double eben deutlich weiter hinter dem eigentlichen Ende und man trifft zufällig etwas kritisches, während es beim double zufällig gut geht.
    2. Vielleicht liegt das long double Feld einfach woanders als das double Feld, weil es nicht dahin passte, wo sonst das double Feld gelegen hat. Zufällig ist dadurch nun hinter dem Feld ein kritischer Bereich.
    3. Vielleicht ist der vector beim double ganz anders aufgebaut. Vielleicht hat er eine Optimierung für kleine Felder. double passt für die Optimierung, long double passt nicht. Dadurch befindet sich hinter dem long double Feld zufällig ein kritischer Bereich, hinter dem double-Feld nicht.

    Kann natürlich auch an 1000 anderen Dingen liegen, undefiniertes Verhalten ist eben undefiniert. Darf auch funktionieren. Ändert natürlich nichts da dran, dass das Programm dann trotzdem falsch ist.



  • Hallo Sepp,

    bei mir ist long double 16 bit und double 8 bit. Demnach hast du schonmal in dieser Annahme recht. Jedenfalls habe ich das nun korrigiert. Da bin ich ja gespannt ob ich irgendwo weitere Fehler hab, die erst zur Laufzeit auftreten (ich hoffe nicht) aber sowas schleicht sich denk ich bei jedem ein, jedoch sollte es natürlich keinesfalls die Regel sein.

    Danke nochmals.



  • Shor-ty schrieb:

    Hallo Sepp,

    bei mir ist long double 16 bit und double 8 bit.

    Byte 😉


  • Mod

    Falls du konsequent STL-Container benutzt, sollte sich mit den Tipps, die ich dir gegeben habe, schon einmal jede Menge automatisieren lassen. Wenn ein Programm mit Debug-STL keine Fehler wirft, sauber durch valgrind läuft und das erwartete Ergebnis liefert, ist das schon einmal ein ziemlich gutes Zeichen, dass es technisch korrekt ist.



  • Ich hab mir mal schnell valgrid installiert und mit deinen Attributen mein Programm gestartet. Das Resultat war wie folgt:

    shorty@src: valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes ./automaticFlameletCreator -transport ../files/fullTransport.tra -thermodynamic ../files/fullThermo.tdc -chemistry ../files/kinetics.kin -AFCDict ../files/afcDict
    ==6696== Memcheck, a memory error detector
    ==6696== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
    ==6696== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
    ==6696== Command: ./automaticFlameletCreator -transport ../files/fullTransport.tra -thermodynamic ../files/fullThermo.tdc -chemistry ../files/kinetics.kin -AFCDict ../files/afcDict
    ==6696== 
    /*------------------------------------------------------------------------*\
    |  c-o-o-c-o-o-o             |                                             |
    |  |     |     A utomatic    | AFC: The Open Source Flamelet Toolbox       |
    |  c-o-o-c     F lamelet     | Version: 1.0.0                              |
    |  |     |     C onstructor  | Web: www.Holzmann-cfd.de                    |
    |  c     c-o-o-o             |                                             |
    \*------------------------------------------------------------------------*/
    
     c-o Reading chemistry data
    
     c-o Reading thermodynamic data
    
     c-o Reading transport data
    
     c-o Reading AFCDict
    
     c-o All data read successfully
    
     c-o Check transportData (all species available)
    
     c-o Check thermodynamicData (all species available)
    
     c-o Check if species used in afcDict are available
    
     c-o Data O.K.
    
     c-o Overview of Look-Up-Tables ...
    
        ... for adiabatic condition
    
            Create flamelets for scalar dissipation rate 1e-06 Hz
    
     c-o Start flamelet calculation
    
        c-o Calculate Look-Up-Table with defect: 0 J/kg
    
          c-o Calculate flamelet for 1e-06 Hz
          --------------------------------------------
            Time: 0 s
            Time: 0.001 s
            Time: 0.002 s
            Time: 0.003 s
            Time: 0.004 s
    
     c-o Calculation done in 4.216866s
    
    Destructor TransportData
    Destructor ThermoData
    ==6696== 
    ==6696== FILE DESCRIPTORS: 3 open at exit.
    ==6696== Open file descriptor 2: /dev/pts/11
    ==6696==    <inherited from parent>
    ==6696== 
    ==6696== Open file descriptor 1: /dev/pts/11
    ==6696==    <inherited from parent>
    ==6696== 
    ==6696== Open file descriptor 0: /dev/pts/11
    ==6696==    <inherited from parent>
    ==6696== 
    ==6696== 
    ==6696== HEAP SUMMARY:
    ==6696==     in use at exit: 0 bytes in 0 blocks
    ==6696==   total heap usage: 95,658 allocs, 95,658 frees, 4,004,219 bytes allocated
    ==6696== 
    ==6696== All heap blocks were freed -- no leaks are possible
    ==6696== 
    ==6696== For counts of detected and suppressed errors, rerun with: -v
    ==6696== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
    

    Sofern ich das richtig interpretiere, scheint alles gut zu sein aber es sind 3 Dateien am Ende des Programms noch geöffnet.

    Werd mich diesbezüglich mal kundig machen. Scheint mir ein sehr geniales Tool zu sein (grad die Angabe mit Memory Leaks find ich sehr interessant).


  • Mod

    Die 3 Dateien sind stdin, stdout, und stderr. Das ist normal, dein Programm ist (valgrind-)fehlerfrei.



  • wob schrieb:

    KeineAhungAber? schrieb:

    meines wissens nach kommt bei deinem beispiel 0 - 1 heraus --> was dann vector[-1] wäre...

    wo wäre hier der sinn? (oder der sehr große wert?)

    Nein, es kommt nicht 0 - 1 = -1 heraus, sondern (size_t)0 - 1 = was_grosses. Und size_t ist vorzeichenlos und da gibt es kein -1, sondern du hast einen Überlauf auf die größte Zahl je nach Größe von size_t, zum Beispiel auf 18446744073709551615 bei 64 bit.

    ach so - klar 🙂

    sehr interessant hab ich nicht gewusst... 🙂 - konnte es gestern leider auch nicht testen 🙂 .

    ok, jedoch muss auch hier geprüft werden ob der vector leer ist...

    vector<int> v;
    	cout << "Size: " << v.size() - 1 << "\n";
    	cout << "Vektor: " << v[v.size()-1] << "\n"; // 18446744073709551615
    

    vielen dank für die beantwortung meiner frage 🙂

    lg


Anmelden zum Antworten