Segmentation Fault - wie der Sache auf die Schliche kommen?



  • Servus,

    ich habe versucht ein Programm von Java nach Cpp zu hieven. Nun habe ich mal das Ungetüm laufen lassen und der Build hat schon mal geklappt. Leider wird beim Run ein Segmentation Fault ausgelöst - wie kann ich der Sache nun auf die Schliche kommen - aktuell sehe ich nur das Programm häppchenweise ans Laufen zu bekommen und so nach und nach den Fehler einzugrenzen. Wie geht ihr vor, wenn ihr mit einem Segmentation Fault zu kämpfen habt?

    IDE ist Netbeans 6.9.1 und System ist Ubuntu 10.04 - Danke vorab!


  • Mod

    Jay1980 schrieb:

    ich habe versucht ein Programm von Java nach Cpp zu hieven. Nun habe ich mal das Ungetüm laufen lassen und der Build hat schon mal geklappt. Leider wird beim Run ein Segmentation Fault ausgelöst - wie kann ich der Sache nun auf die Schliche kommen - aktuell sehe ich nur das Programm häppchenweise ans Laufen zu bekommen und so nach und nach den Fehler einzugrenzen. Wie geht ihr vor, wenn ihr mit einem Segmentation Fault zu kämpfen habt?

    Die zweite Anlaufstelle ist ein Build gegen die Debugruntimebibliothek.

    Die erste Anlaufstelle ist natürlich der Debugger.

    Die nullte Anlaufstelle ist bei großen Projekten, dass valgrind keinerlei Fehler findet. Und zwar nicht nur Speichermanagementfehler (man soll so programmieren, dass diese gar nicht erst auftreten, siehe unten), sondern auch keine uninitialisierte Variablen und alles andere was valgrind sonst noch so finden kann.

    Die -1. Anlaufstelle ist, dass der Compiler keinerlei Warnungen zeigt auf höchster Warnstufe.

    Die -2. Anlaufstelle ist, so zu Programmieren, dass Logikfehler schon vom Compiler erkannt werden, siehe Punkt -1.

    edit: Und ein paar optionale Debugausgaben(die man beim Build wahlweise an- oder ausschalten kann), sind auch immer nützlich. Gehören bei großen Projekten zu Punkt -2.



  • Da hilft nur systematisches Testen/Debuggen. DU musst halt versuchen Programmcode auszuschließen bis du den Fehler findest. Aber da du einen Zugriffsfehler hast, würde ich bei Arrays beginnen. Ohne Code etc. bekommst du wohl nicht viel hilfe und musst einfach suchen.



  • Habe die Fehlerursache wohl etwas eingegrenzt - es scheint damit zu tun zu haben, wenn ich einen Vektor als Parameter an den Konstruktor übergebe.

    out += " Eingelesene Sequenzen: \n";
        vector<string> seqs = javamain.getSequencesList();
        for ( int i = 0; i < seqs.size(); i++ )
        {
            out += " " + seqs.at( i ) + "\n";
        }
        out += "\n";
    
        // Aufruf
        H2r h2r( seqs ); // TODO Check ob aendern auf callbyreference
        //h2r.computeH2r();
    

    Wenn ich die h2r-Instanz nicht erzeuge läuft es ohne Fault, sonst knallts!


  • Mod

    Jay1980 schrieb:

    Habe die Fehlerursache wohl etwas eingegrenzt - es scheint damit zu tun zu haben, wenn ich einen Vektor als Parameter an den Konstruktor übergebe.

    Warum zeigst du uns dann nicht den Konstruktor?

    edit: Und verlass dich nicht zu sehr auf "Wenn ich dies rausmache, dann geht es". Speicherfehler verursachen sehr oft undefiniertes Verhalten, da kann man sich nicht auf solche Vorgehensweisen verlassen.



  • Okay, hier ist der Teil des Konstruktors der gleich den Fehler hervorruft:

    #include "h2r.h"
    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    H2r::H2r( vector<string> seqList ){
    
        // Debuglevel
        extern string log;
        if ( log == "1" ){
            cout << "DEBUG: H2r - Konstruktor() ... ";
        }
    

    Die Variablen log ist in der Datei main.cpp angelegt - ich wollte diese als globale Variable, damit diese von überall zugreifbar ist.

    // Konfiguration hier und in Javamain-Objekt
    //const string log = "0"; // keine Ausgaben anzeigen
    const string log = "1"; // volles Logging
    //const string test = "0"; // keine Tests
    const string test = "1"; // alle Regressionstests ablaufen lassen
    string out = "";
    
    // Funktionsprototypen
    void testJavamainGetSequencesList();
    
    /*
     * 
     */
    int main(int argc, char** argv) {
    
        // Konfigurationsobjekt holen
        Javamain javamain; // Konstruktor Breymann2009 S. 155, globale Variable
    
        if ( test == "1" )
        {
            // Behelfs-Testsuite
            cout << "\n";
            cout << " ************************************** \n";
            cout << "  Testmodus H2r - HSRP-Ermittlung \n";
            cout << " ************************************** \n";
            cout << "\n";
    
            // Testmethoden
            testJavamainGetSequencesList();
        }
    
        if ( log == "1" )
        {
            // Debugversion Breymann S. 133
            cout << "\n";
            cout << " ************************************** \n";
            cout << "  Log-Modus H2r - HSRP-Ermittlung \n";
            cout << " ************************************** \n";
            cout << " \n ";
        }
    


  • Jay1980 schrieb:

    out += " Eingelesene Sequenzen: \n";
        vector<string> seqs = javamain.getSequencesList();
        for ( int i = 0; i < seqs.size(); i++ )
        {
            out += " " + seqs.at( i ) + "\n";
        }
        out += "\n";
    

    Du weisst, dass das ineffizient ist (weil strings nicht gebuffert werden) und das nächste mal, wenn du alles neu schreibst, besser so geschrieben gehört:

    std::ostringstream out;
    out << " Eingelesene Sequenen: ";
    for (std::vector<std::string>::iterator it=seqs.begin(); it!=seqs.end(); ++i)
        out << "\n " << *it;
    out << "\n\n";
    

    oder noch besser:

    std::ostringstream out;
    out << " Eingelesene Sequenen: \n ";
    std::copy(seqs.begin(), seqs.end(), std::ostream_iterator(out, "\n ");
    out << "\n";
    


  • Der Fehler liegt IMHO beim extern .
    Wenn du diese Zeile aus dem Konstruktor rausnimmst, und unter dein hässliches `

    using namespace std;` schreibst, sollte es gehen.

    Vllt. geht es auch, wenn du es am Ort lässt und extern string ::log; schreibst, aber da kenne ich mich nicht so genau aus.
    Globale Variablen deklariert man eh lieber in einem Headerfile, aber meistens kapselt man die über eine Funktion mit einem statischen Objekt. Dass alles nicht threadsafe ist, macht bei dir vermutlich nichts aus.



  • Danke ich werde direkt etwas nachlesen. Hast du vielleicht einen Link zu einem Tutorial oder Artikel, der zeigt wie man effizient mit Strings arbeitet - ich habe schon etwas geschaut, aber es ist für mich schwierig aufgrund der Vielzahl von String-Bibliotheken die Übersicht zu behalten, oft kommen bei Google auch die Unterlagen an vorderster Stelle, die relativ alt sind - da kann ich nicht einschätzen, ob die noch aktuell sind.

    Auch die Sache mit den globalen Daten in eigenen Headerfiles lese ich nach - da sollte ich leichter was finden.

    Ich habe gelesen using namespace ist voll ok in cpp-Dateien und nur in h-Dateien soll man dies nicht nutzen?!



  • Am besten garnicht using namespace verwenden. using std::... reicht auch für einzelne Funktionen. Und wie du sagtest, nie im Header verwenden 🙂


  • Mod

    HighLigerBiMBam schrieb:

    Am besten garnicht using namespace verwenden. using std::... reicht auch für einzelne Funktionen

    Warum nicht?



  • Um Namenskonflikte zu vermeiden. Es ist durchaus möglich, dass es in zwei verschiedenen Namensräumen(Bibliotheken) Funktionen mit gleichen Parametern gibt, die auch gleich heißen. Und eigene Funktionen mit z.B. den namen copy oder swap sind schnell geschrieben und using namespace std ist da sicher nicht hilfreich 😉



  • Jay1980 schrieb:

    Ich habe gelesen using namespace ist voll ok in cpp-Dateien und nur in h-Dateien soll man dies nicht nutzen?!

    Solche Aussagen sind wie alle generalisierenden Aussagen völliger Blödsinn. (Wenn auch diese natürlich keine schlechte Ausgangslage ist). Aber viel wichtiger ist, zu verstehen, was using denn genau bedeutet rsp. bewirkt.
    Dann folgt alles andere wie selbstverständlich.
    Das Problem mit using ist, dass du bei grösseren Projekten mit mehreren namespaces arbeiten wirst und entsprechend schnell ein kleines Chaos bekommst, welches Objekt jetzt zu welchem namepace gehört. (Weil da dann eben nur string steht, und nicht std::string, kannst du dann im File suchen gehn, welchen namespace dass du denn jetzt wieder gerade verwendest und um welches Objekt dass es sich wirklich handelt).
    In den .cpp-files ist das noch einigermassen problemlos möglich, aber wenn du die Direktive in nem Header File verwendest, dann musst du um die namespace-Frage zu klären auch immer noch gleich alle header files durchforsten, die allenfalls selber wieder headerfiles included haben usw usw... du siehst das Problem? Ausserdem erinnerst du dich zu einem späteren Zeitpunkt unter Umständen nicht mehr, dass du das using im header hast, includest deine geile alte Klasse in ein neues Projekt und weil du da mit namespaces wieder fahrig umgegangen bist, bringt dir die using direktive ausm alten header deine ganzen neuen namespaces durcheinander... Und du wunderst dich dann warum plötzlich nicht mehr auf das Objekt zugegriffen wird das doch vorher gerade noch so prima funktioniert hat...


  • Mod

    HighLigerBiMBam schrieb:

    Um Namenskonflikte zu vermeiden. Es ist durchaus möglich, dass es in zwei verschiedenen Namensräumen(Bibliotheken) Funktionen mit gleichen Parametern gibt, die auch gleich heißen. Und eigene Funktionen mit z.B. den namen copy oder swap sind schnell geschrieben und using namespace std ist da sicher nicht hilfreich 😉

    Und? Dann benutze ich in dem Fall eben kein using. Aber warum sollte ich mir bei überschaubaren Quelltexten diese Bequemlichkeit nicht gönnen? Es ist ja eher selten, dass man in einer einzelnen Quelltextdatei mehr als 1-2 verschiedene Namensräume benutzt.


Anmelden zum Antworten