Wo ist der Speicherzugriffsfehler???



  • Hi,

    ich versuche mich zur Zeit an einer Konsolen-Oberfläche für ein Webradio.

    Allerdings kriege ich einen Speicherzugriffsfehler, weiß nicht wodurch er ausgelöst wird und bin am Rande der Verzweiflung, weil ich nicht weiß, wie ich ihn ausmerzen kann.

    #include<iostream>
    //#include<fstream>
    #include<string>
    #include<stdio.h>
    #include<cstdlib>
    #include <sqlite3.h>
    
    #define MARKUSED(X) ((void)(&(X)))
    #define READLINE(X) getline(cin, X)
    using namespace std;
    
    sqlite3 *db;
    
    class Sender {
    	public:
    		string name;
    		string url;
    	int id;
    	/*operator string() {
    
    		return string("Sender {%nurl: "+url+"%nname: " + name + endl + "%n}");
    	}
    	// Hierran muss noch gearbeitet werden: stringstreams etc.*/
    };
    
    Sender* senders;
    int anzahl_senders = 0;
    int j = 0;
    
    void insert_db(Sender s) {
    	string sql = "INSERT INTO sender(name, url) " \
    				"VALUES (\"" + s.name + "\", \"" + s.url + "\");";
    	char *errMsg = 0;	
    	if( sqlite3_exec(db, sql.c_str(), 0, 0 , &errMsg) != SQLITE_OK ) {
    		fprintf(stderr, "SQL error: %s\n", errMsg);	
    	} else {
    		printf("Erfolgreich eingetragen mit folgendem SQL: %s", sql.c_str());
    	}
    }
    
    static int callback_ausgabe(void *data, int argc, char **argv, char**azColName) {
    	int i;
    	MARKUSED(data);
    
    	//fprintf(stderr, "%s: ", (const char*)data);
    	if(j == 0){	
    		senders = new Sender[argc];
    
    	}
    	Sender loc_s;
    	for(i = 0; i < argc; i++) {
    		if(string(azColName[i]) == string("name")){
    			loc_s.name = argv[i];
    		} else if(string(azColName[i]) == string("url")) {
    			loc_s.url = argv[i];
    		} else if(string(azColName[i]) == string("id")) {
    			loc_s.id = atoi( argv[i] );
    		}
    	}
    	anzahl_senders++;	
    	senders[j] = loc_s;
    	j++;
    	return 0;
    }
    
    int main(int arg_num, char *argv[]) {
    	string args[arg_num];
    
    	if(sqlite3_open("datenbank.db", &db)) {
    		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
    		exit(0);
    	}
    
    	cout << "Argnum:" << arg_num << endl;
    
    	for(int i = 0; i < arg_num; i++) {
    		args[i] = string(argv[i]);
    	}
    	for(int i = 0; i < arg_num; i++) {
    		cout << "<test>";
    		cout << "args[" << i << "] = \"" << args[i] << "\";";
    		cout << "</test>"<< endl;	
    	}
    	string s = "TEST";
    	cout << s;
    	if(arg_num > 1){
    		if(args[1] == "add_host"){
    			//host hinzufügen
    			cout << "Unter welchen Namen soll der Sender gespeichert werden? ";
    			Sender sender;
    			//getline(cin, sender.name);
    			//cin >> sender.name;
    			READLINE(sender.name);
    			cout << "Bitte URL eingeben!";
    			cin >> sender.url;
    			insert_db(sender);
    			return 0;
    		}
    	} else {
    		cout << "kein host wird hinzugefügt";
    	}
    	//Hier ist noch platz für andere kommandos.
    	//die Sender werden in der globalen Variable senders[] gespeichert. {
    	cout << "Hallo Welt";
    	string sql = "SELECT * FROM sender";
    
    	char *zErrMsg = 0;
    
    	cout << "Funktioniert vor abfrage";
    	if(sqlite3_exec(db, sql.c_str(), callback_ausgabe, (void*) 0, &zErrMsg) != SQLITE_OK ) {
    		fprintf(stderr, "SQL error: %s\n", zErrMsg);
    		return 0;
    	}
    	//}
    	if(arg_num > 2 && args[1] == "play") {
    		string wunsch_name = args[2];
    		for(int i = 0; i < anzahl_senders; i++) {
    			if(senders[i].name == wunsch_name) {
    				FILE *fp;
    
    				fp = popen((string("cvlc ")+senders[i].url).c_str(), "w");
    
    				if(fp == NULL) {
    					printf("\n Failes command \n");
    					return 0;
    				}
    			}
    		}
    	}
    	cout << anzahl_senders << " Sender gefunden:";
    	for(int i = 0; i < anzahl_senders; i++) {
    		cout << "Sender: " << senders[i].id << ", " << senders[i].name << ", " << senders[i].url << endl; 
    	}
    
    	//if(!file_exists("db.sqlite")) {
    	//		cout << "Datenbank existiert nicht! Wird angelegt.";
    	//
    	//}
    
    	return 0;
    }
    

    ausgabe:

    felix@linux-k3ox:~/ich_lerne_c++/selber/radio> ./program
    Argnum:1
    <test>args[0] = "./program";</test>
    Speicherzugriffsfehler
    felix@linux-k3ox:~/ich_lerne_c++/selber/radio>
    

    der "interessante" Teil ist in Z.82-84:

    for(int i = 0; i < arg_num; i++) {
    		cout << "<test>";
    		cout << "args[" << i << "] = \"" << args[i] << "\";";
    		cout << "</test>"<< endl;	
    	}
    	string s = "TEST";
    	cout << s;
    

    es werden alle Argumente ausgegeben, die dem Programm übergeben wurden, es wird kein weiteres mal "<test>" ausgegeben, was ja heißt, dass es jedes Mal gelingt, auf die Array-Felder zuzugreifen, aber der string "TEST" wird nicht mehr ausgegeben.... Ich kann mir nicht erklären, wieso.

    (weder -Wall, noch -Wextra geben beim Kompilieren etwas aus)

    Vielen Dank für die Hilfe.



  • ich bezweifle sehr dass das einen unterschied macht aber schreib mal statt "arg_num" (der erste parameter des mains) "argc" hin (wie es laut standard üblich ist).



  • doktor banane schrieb:

    ich bezweifle sehr dass das einen unterschied macht aber schreib mal statt "arg_num" (der erste parameter des mains) "argc" hin (wie es laut standard üblich ist).

    Danke für die Antwort, aber es hat keinen Unterschied gemacht.



  • Du musst doch wissen wo er herkommt?

    Hast du keinen Compiler bei dem du nachgucken kannst?

    Einfach BreakPoints setzen und mit Continue Schritt fuer Schritt durch das Programm gehen.



  • was muss denn dem aufruf an sqllite3_open uebergeben werden. Der Zeiger auf eine Struktur (fuer die schon Speicher da ist) der nur die Adresse eines Zeigers, der dann gefuellt wird.

    Du uebergibst die Adresse eines Zeigers und dahinter liegt kein allozierter Speicher, sondern der Zeiger zeigt irgendwo hin.



  • Ruvi schrieb:

    Du musst doch wissen wo er herkommt?

    Hast du keinen Compiler bei dem du nachgucken kannst?

    Einfach BreakPoints setzen und mit Continue Schritt fuer Schritt durch das Programm gehen.

    leanderlaepple schrieb:

    der "interessante" Teil ist in Z.82-84:

    for(int i = 0; i < arg_num; i++) {
    		cout << "<test>";
    		cout << "args[" << i << "] = \"" << args[i] << "\";";
    		cout << "</test>"<< endl;	
    	}
    	string s = "TEST";
    	cout << s;
    

    es werden alle Argumente ausgegeben, die dem Programm übergeben wurden, es wird kein weiteres mal "<test>" ausgegeben, was ja heißt, dass es jedes Mal gelingt, auf die Array-Felder zuzugreifen, aber der string "TEST" wird nicht mehr ausgegeben.... Ich kann mir nicht erklären, wieso.

    (weder -Wall, noch -Wextra geben beim Kompilieren etwas aus)


  • Mod

    doktor banane schrieb:

    ich bezweifle sehr dass das einen unterschied macht aber schreib mal statt "arg_num" (der erste parameter des mains) "argc" hin (wie es laut standard üblich ist).

    Unsinn.

    Zum Problem des Threaderstellers: Ich sage da mal lieber nicht viel dazu. Ich würde alles wegschmeißen, es ist alles falsch, was man falsch machen kann. Wie hast du C++ gelernt?



  • SeppJ schrieb:

    doktor banane schrieb:

    ich bezweifle sehr dass das einen unterschied macht aber schreib mal statt "arg_num" (der erste parameter des mains) "argc" hin (wie es laut standard üblich ist).

    Unsinn.

    Zum Problem des Threaderstellers: Ich sage da mal lieber nicht viel dazu. Ich würde alles wegschmeißen, es ist alles falsch, was man falsch machen kann. Wie hast du C++ gelernt?

    Naja, bisher bin ich am lernen 🙂
    - aber wenn du meinst, versuch ich's heut Abend ganz von vorn.

    LG Felix


  • Mod

    leanderlaepple schrieb:

    Naja, bisher bin ich am lernen 🙂
    - aber wenn du meinst, versuch ich's heut Abend ganz von vorn.

    Nicht einfach so neu versuchen. Wenn du wüsstest, wie es richtig geht, hättest du es doch hoffentlich gar nicht erst so gemacht, oder? Daher nehme ich an, dass du es nicht richtig kannst. Was heißt, du hast es falsch gelernt. Wo hast du gelernt?

    Um etwas konkreter zu werden: Die eine Hälfte des Codes ist eine ganz grauenhafte Mischung aus C und C++. Und zwar die Teile gemischt, die man nicht mischen darf, weil es zu Speicherzugriffsfehlern und ähnlichem kommt (ob das wohl etwas mit deinem Problem zu tun hat?). Die andere Hälfte sieht aus wie aus dem Internet kopiert und nicht verstanden, was sie tut. Copy & Paste ist nicht programmieren! Und als Sahnehäubchen ist alles noch furchtbar umständlich und verworren, als ob du einfachste best practices nicht kennst (was du vermutlich wirklich nicht tust, sonst würdest du es nicht so machen). Globale Variablen, Zeigergefrickel, manuelle Speicherverwaltung, Makros, … ich könnte hier noch Seiten mit Kritik füllen.
    Es scheint dir an den nötigen Grundlagen zu fehlen, um dieses Programm sauber schreiben zu können. Und ein Programm wie dieses funktioniert nicht, wenn es nicht sauber geschrieben ist.



  • SeppJ schrieb:

    Nicht einfach so neu versuchen. Wenn du wüsstest, wie es richtig geht, hättest du es doch hoffentlich gar nicht erst so gemacht, oder?

    Daher nehme ich an, dass du es nicht richtig kannst. Was heißt, du hast es falsch gelernt. Wo hast du gelernt?

    Meine Programmier-Geschichte könnte besser aussehen: Ich habe vor Jahren mit PHP angefangen, bin dann irgendwann auf Java (nicht JavaScript, soo blöd, das zu verwechseln, bin nicht mal ich 😉 ) umgestiegen, und beschäftige mich eben seit vllt 1/2 Jahr mit C++. Das heißt, wirklich C++ gelernt habe ich eigentlich nie. Ich versuche zur Zeit halt, ein paar Projekte umzusetzen, um mich an C++ zu gewöhnen und mich darin zu üben.

    Mein eigentliches Problem war, dass ich kein wirklich gutes C++-Tutorial zu SQLite gefunden hab, ich weiß nicht genau, ob es überhaupt eine C++-lib für SQLite gibt (und da ich mich in C wirklich gar nicht auskenne, habe ich in der Tat viel cpoy-pasted. Welche Datenbank kannst du/könnt ihr mir empfehlen, in der ich mit C++ arbeiten kann? Es wär halt schon praktisch, wie in SQLite direkt in Dateien zu schreiben, die sich im selben Ordner befinden.

    Da ich ja nicht mit großen Datenmengen arbeite, wäre natürlich XML auch eine Möglichkeit, die Sachen zu speichern - würdet ihr mir XML, für das es ja einige C++-libs geben wird, empfehlen?

    Und gibt es nicht ein gutes Rezept, um "Speicherzugriffsfehler" zu finden?


  • Mod

    leanderlaepple schrieb:

    Und gibt es nicht ein gutes Rezept, um "Speicherzugriffsfehler" zu finden?

    Die eigentlich richtige Technik wäre es, so zu programmieren, dass sie gar nicht erst vorkommen. Würdest du eben nicht C und C++ mischen, sondern dich streng an die in C++ üblichen Techniken halten, ist es praktisch unmöglich, solche Fehler zu erzeugen. Falls man sie ausnahmesweise doch hat, würde man den Debugmodus der STL anschalten. Aber die benutzt du ja nicht, daher geht das hier nicht. Daher bleibt hier als Patentlösung das Nutzen von Debugger und Programmen wie valgrind. Damit findet man solche Fehler auch sehr schnell. Testausgaben sind zwar nett, aber nicht zuverlässig genug (der Fehler passiert meist ganz woanders als der Absturz) und zudem recht aufwändig in der Umsetzung.

    C++ und SQL: Wenn man eine C-Schnittstelle hat (die in der Regel ja schon objektorientiert sind, bloß aber eben im C-Stil, bei dem die ganze Verantwortung beim Anwender liegt), dann schriebt man sich normalerweise einen netten kleinen Wrapper drumherum. Oder guckt, ob andere das schon getan haben (Tipp: SQL und C++ sind beide sehr verbreitet, das hat garantiert schon jemand getan). Dann hat man wieder die oben genannte Sicherheit, gar nichts mehr falsch machen zu können. Sofern der Wrapper richtig programmiert ist. Was wieder zu dem Problem führt, dass du das vermutlich nicht richtig programmieren könntest. C++ lernt man eben nicht aus Tutorials und Zusammenklicken von Codefetzen. Es ist eine sehr schwere Sprache. Das muss man gründlich lernen, bevor man in den Genuss der Vorteile kommt, ansonsten bekommt man nur Fehler (so wie hier), da die Sprache keinerlei Sicherheitsnetze für experimentierende Anfänger vorsieht. Das ist einfach so, da kann man nichts dran ändern.



  • @leanderlaepple:
    Mach dir nix daraus, ich lerne auch gerade C++. Lernen tut der Mensch NUR durch Fehler machen und diese muss man dann Stueck fuer Stueck minimieren.

    Du kannst nicht C++ nur aus Buechern oder Tuts lernen, sondern das ist ein Gemeinschaftswerk aus Grundlagen bueffeln und viel Probieren. Und immer ganz klein anfangen, sonst ist der Frust bald groesser als die Motivation.

    Hier sein Werk rein zu stellen und dann mal von den Profis richtig auseinander nehmen zu lassen, finde ich aber eine gute Idee. Das erweitert mit Sicherheit den Horizont ganz schoen.



  • Gibt etliche C++ Wrapper für SQLite.
    Google findet die auch sogar.

    Weil ich aber mal nicht so sein will, hier nen Link wo einige aufgelistet sind: http://stackoverflow.com/questions/120295/what-is-a-good-oo-c-wrapper-for-sqlite

    ps: Und was das Finden von Access-Violations angeht... kann das dein Debugger nicht? Check dir ne vernünftige C++ IDE mit Debugger-Integration. Dann sollte sich der Aufwand darauf beschränken auf den "Play" Button der IDE zu klicken.



  • leanderlaepple schrieb:

    ausgabe:

    felix@linux-k3ox:~/ich_lerne_c++/selber/radio> ./program
    Argnum:1
    <test>args[0] = "./program";</test>
    Speicherzugriffsfehler
    felix@linux-k3ox:~/ich_lerne_c++/selber/radio>
    

    der "interessante" Teil ist in Z.82-84:

    for(int i = 0; i < arg_num; i++) {
    		cout << "<test>";
    		cout << "args[" << i << "] = \"" << args[i] << "\";";
    		cout << "</test>"<< endl;	
    	}
    	string s = "TEST";
    	cout << s;
    

    es werden alle Argumente ausgegeben, die dem Programm übergeben wurden, es wird kein weiteres mal "<test>" ausgegeben, was ja heißt, dass es jedes Mal gelingt, auf die Array-Felder zuzugreifen, aber der string "TEST" wird nicht mehr ausgegeben.... Ich kann mir nicht erklären, wieso.

    Dass "TEST" nicht mehr ausgegeben wird, wird daran liegen, dass er (und vermutlich auch einige Ausgaben danach) nicht sofort auf die Konsole, sondern in std::cins Streambuffer oder den Konsolenbuffer geschrieben werden und das Programm abstürzt, bevor sie angezeigt werden. Schreib mal

    cout << s << flush;
    

    , dann wird deine Ausgabe anders aussehen.

    Meine Vermutung ist, dass der Fehler daher kommt, dass du den zweiten Parameter der Callback-Funktion als Anzahl der Zeilen im Ergebnis missverstehst, wo es um die Anzahl der Spalten geht.


Log in to reply