Problem mit Funktionsaufruf in Signal

Du bist neu in der Welt von C++? Dann schau hier herein!
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Problem mit Funktionsaufruf in Signal

Beitrag von Darwin »

Hallo Forum,

ich arbeite Gerade an einer QT Oberfläche mit ein paar Objekten.
Bei Betätigung dieser werden Signale ausgelöst, welche Methoden aufrufen. In diesen werden wiederum Methoden eines anderen Objektes (mq) ausgeführt.

Bsp:

Code: Alles auswählen

connect(buttonLCDClear, SIGNAL(clicked()), this, SLOT(FunctionLCDClear()));
  connect(comboSummer, SIGNAL(currentIndexChanged(int)), this, SLOT(BeeperStateChanged(int)));
Und die Methoden dazu:

Code: Alles auswählen

void Miniterminal::FunctionLCDClear(void)
{  mq.addDataPacket(_terminalID,_receiverID,COMMAND,SET_BEEPER,0,"",STF,ENF);
}

void Miniterminal::BeeperStateChanged(int i)
{
mq.addDataPacket(_terminalID,_receiverID,COMMAND,SET_BEEPER,0,"",STF,ENF);
}
In der Methode addDataPacket von mq werden die Daten in eine Liste eingetragen (das Funktioniert ).

Mein Problem:
Der addDataPacket Aufruf in der FunctionLCDClear-Methode funktioniert (meistens), der addDataPacket Aufruf in der BeeperStateChanged-Methode führt unweigerlich zum Absturz des Programms (xxx.exe hat ein Problem festgestellt und muss beendet werden).

Was mich wundert: Wenn ich den Code der Methode addDataPacket entferne, gibt es keinen Absturz. Wenn ich nach addDataPacket ein Sleep(5 sekunden); einfüge, Dauert es 5 Sekunden bis das Programm abstürzt.

Ich bin echt Ratlos (da noch Anfänger in QT). Vielleicht hat von Euch ja wer einen Einfall.

Wäre echt Dankbar, da ich jetzt schon ein paar Stunden versuche das Problem zu lösen.

Danke,
Michael

EDIT:
QT 4.5.1 / MinGW
WinXP Pro

Gibt es eigentlich in der kostenlosen Version irgendeine Möglichkeit zum Debuggen?
speedy
Beiträge: 52
Registriert: 23. Juli 2008 03:17

Beitrag von speedy »

Code: Alles auswählen

void Miniterminal::BeeperStateChanged(int i)
{
mq.addDataPacket(_terminalID,_receiverID,COMMAND,SET_BEEPER,0,"",STF,ENF);
}
Kennt dieser Aufruf denn die ganzen Variablen?
Sind das globale Variablen in der Klasse?
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

ja, variablen sind bekannt

Beitrag von Darwin »

Hallo!

Ja, die Variablen sind bekannt, der Kompiler meldet keinen Fehler und der Aufruf klappt in der Methode von LCDClear.

Danke,
Michael
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

OK, klär uns auf :)
sind COMMAND, SET_BEEPER, STF und ENF Variablen, Konstanten oder Macros?
Sind die Variablen auch korrekt initialisiert?
Zeig uns mal die Implementierung von addDataPacket().
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Gibt es eigentlich in der kostenlosen Version irgendeine Möglichkeit zum Debuggen?
Falls du "gdb" (GNU-Debugger) noch nicht hast, einfach ab http://sourceforge.net/projects/mingw/files dazuinstallieren.
Danach ab der Kommandozeile mit "gdb deinprogramm.exe" laden, mit "run" starten und nach dem Crash mit "where" anschauen, wo das Programm steht.
Ich bin echt Ratlos (da noch Anfänger in QT). Vielleicht hat von Euch ja wer einen Einfall.
Weil der Code nichts auffälliges zeigt, muss der Fehler schon vorher (z.B. unsaubere Pointeroperationen) oder in addDataPacket() produziert worden sein. Es gibt eigentlich folgende zwei Möglichkeiten:

1. mit "gdb" den Fehler suchen
2. das Programm immer weiter reduzieren ( so dass der Crash bleibt) und dann entweder den Fehler selbst erkennen oder hier ein Beispielprojekt posten...
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Beitrag von Darwin »

Hallo und Danke für Eure bisherigen Beiträge!

COMMAND, SET_BEEPER, STF und ENF sind Markos (zB #define COMMAND 0x02) Diese stehen im MQueue Header (MQueue - Davon wird auch das Objekt mq erzeugt).

Methode addDatapacket:

Code: Alles auswählen

void MQueue::addDataPacket(unsigned char terminalID, unsigned char receiverID, unsigned char commandtype, unsigned char command1, unsigned char command2,const char *data, unsigned char stf, unsigned char enf)
{
    // Neuen Link-Zeiger erstellen welcher auf das erste Element zeigt
    DPLink *dpl=firstDataPacket; 

   // So lange weiterspringen bis l auf den letzten vorhandenen Link zeigt
    while(dpl->nextDP!= NULL)     
    {
      dpl=dpl->nextDP;
    }

    // Hier wird das neue Datenpaket eingefügt:
    // Neuen DPLink Zeiger erstellen - dies ist der DPLink Zeiger für das neue Datenpaket
    DPLink *newDataPacket=new DPLink;
    // Nach diesem Link gibt es keinen Link mehr
    newDataPacket->nextDP=NULL;        

    // Hier wird das eigentliche Datenpaket (Zeiger darauf) welcher zum Link gehört erstellt
    newDataPacket->dp=new DataPacket(terminalID,receiverID,commandtype,command1,command2,data,stf,enf);

    dpl->nextDP=newDataPacket;  
    // dpl bezieht sich hier noch auf den Link dpl. Da ein neuer Link eingefügt wurde muss der next von dpl nun auf das neue Datenpaket zeigen.
}
Das Struct zu DPLink ( definiert im Header ):

Code: Alles auswählen

struct DPLink
  {
    DataPacket *dp;
	DPLink *nextDP;
  };

  DPLink *firstDataPacket;
Und die initialisierung im Konstruktor von MQueue:
MQueue::MQueue()

Code: Alles auswählen

{
	firstDataPacket=new DPLink;   // New Operator liefert Zeiger!
	firstDataPacket->dp=NULL;     // Mit "->" weil ein Zeiger ist!
	firstDataPacket->nextDP=NULL; // Mit "->" ein Zeiger ist
}
Danke, Michael
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

So viele (ungetestete) Zeiger, da geht 100% was schief :P
Ich bin mir fast sicher hier brennt es:

Code: Alles auswählen

 DPLink *dpl=firstDataPacket; 

    while(dpl->nextDP!= NULL)      
    { 
      dpl=dpl->nextDP; 
    }
Irgend eines der nextDP ist nicht NULL aber das referenzierte Objekt ist futsch...
Falls nicht bitte Konstruktor von DataPacket. Oder am besten dem Link von SOlarix folgen und gdb installieren...

Ist firstDataPacket ein globales Objekt oder ein Member deiner Klasse?

Ich denke da solltest du dir ein eigenes Namensschema angewöhnen. Z.B.:

Code: Alles auswählen

int m_Member, mMember, _member; // Member
int s_staticMember, sStaticMember; // static member
usw. dass man auch erkennt was das jetzt gerade ist...
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Beitrag von Darwin »

Servus,

Variablen die zu einer Klasse gehören beginnen bei mir mit einem Unterstrich _

FirstDataPacket wird im Header angelegt, gleich nach der Deklaration des Structs (siehe oben). Die initialisierung erfolgt im Konstruktor von MQueue (siehe oben)

Ich habe jetzt den code von addDataPacket ausgeblendet und dann Stückchen für Stückchen wieder hinzugefügt. Der Fehler tritt beim erzeugen des Datenpaketes auf:

Code: Alles auswählen

newDataPacket->dp=new DataPacket(terminalID,receiverID,commandtype,command1,command2,data,stf,enf);
Code vom Datapacket-Konstruktor:

Code: Alles auswählen

DataPacket::DataPacket(unsigned char senderID, unsigned char receiverID, unsigned char packetType, unsigned char command1, unsigned char command2, const char *data, unsigned char startFlag, unsigned char endflag)
{
  _startFlag=startFlag;
  _senderID=senderID;
  _receiverID=receiverID;
  _packetType=packetType;
  _command1=command1;
  _command2=command2;
  _endFlag=endflag; 

  delete _data; // Ein mit new reservierter Speicherplatz wird mit delete wieder freigegeben
  _data=NULL; // Den Zeiger auf NULL setzen
  _dataLenght=strlen(data)+1;
  _data=new char[_dataLenght]; // Neuen Speicherplatz reservieren. _data ist der Zeiger darauf
  memcpy(_data,data,_dataLenght); // New buffer | Buffer to copy from | Number of characters to copy
}
Und im header:

Code: Alles auswählen

class DataPacket
{
private:
  char *_data;
public:
  DataPacket(unsigned char senderID, unsigned char receiverID, unsigned char packetType, unsigned char command1, unsigned char command2, const char *data, unsigned char startFlag=0x02, unsigned char endflag=';');
  virtual ~DataPacket(void);
private:
  unsigned char _startFlag;
  unsigned char _senderID;
  unsigned char _receiverID;
  unsigned char _packetType;
  unsigned char _command1;
  unsigned char _command2;
  unsigned char _dataLenght;
  unsigned char _endFlag;
};
Destruktorcode (wird nie aufgerufen):

Code: Alles auswählen

DataPacket::~DataPacket(void)
{
	_data=NULL;
}
Mit der Installation vom GDB happerts leider noch. Ich habe die
gdb-6.8-mingw-3.patch sowie die
gdb-6.8-mingw-3.tar.bz2 heruntergeladen, weiss aber nicht wie sie zum installieren sind :-(

Danke, Mike
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Beitrag von Darwin »

Weitere Eingrenzung:

Der Fehler im Datenpaketkonstruktor hier auf:

delete _data;

Weswegen weiss ich aber nicht, weil das eigentlich so gehen sollte.

Edit: _data ist vom Typ char *, und wird im Datapacket Header deklariert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Sorry aber irgendwie sieht das nach einen kompletten Mischmasch von C und C++ aus... grauenhaft :(

delete _data ist falsch - wenn schon muss es delkete[] _data heissen. Aber eigentlich wollten wir ja C++ und im speziellen Qt benutzen. QByteArray und ein paar Template-Container wie QList usw. wären nicht schlecht.
Ein virtueller dtor für DataPacket ist auch überflüssig usw.

Und nun zum Problem - ne Idee was überhaupt in _data drin steht zu dem Zeitpunkt wenn es crasht??



Und ab damit nach C++ Grundlagen ...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Ich würde auch NIE in einer Methode einen übergebenen Pointer deleten. UARGH, viel Spaß beim Debuggen, wenn du den versehentlich später wieder verwenden willst ;)

Und sttat const char* nimm doch einen ordentlichen String (std::string, QString, ..)

Und ein GLOBALES OBJEKT in einem KONSTRUKTOR initialisieren. Du weißt schon was passieren kann, wenn du mehrere Objekte erstellst, und jedesmal dein globales Objekt neu instanziiert wird? Alte Daten futsch.

Wenn globales Objekt, dann bitte brav im zugehörigen *.cpp initialisieren, oder besser, wenn du darauf bestehst dass nur ein Objekt der Klasse existieren darf, per Singleton realisieren.

Und bei so vielen Pointern solltest du schon schauen dass irgend wann schon ein Destruktor aufgerufen wird ;)
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

private:
char *_data;
Da würde ausserdem noch ein Copy-CTor und ein Assignment-Operator benötigt.. aber eben: Wie bereits erwähnt mit QString, QByteArray und QLinkedList (dann fällt der ganze "DPLink"-Kram weg) arbeiten.


[EDIT]
gdb-6.8-mingw-3.tar.bz2 heruntergeladen, weiss aber nicht wie sie zum installieren sind
einfach entpacken :wink: .. am besten in das Mingw-Verzeichnis, da hast du erstens die gleiche Struktur (gdb.exe wuerde da also z.B. nach c:\mingw\bin entpackt) und zweitens wirst du ja eh den bin-Pfad in der Windowsumgebung konfiguriert haben..
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Beitrag von Darwin »

Wow, mit soviel Kritik habe ich jetzt nicht gerechnet, aber man will ja lernen.

>>Sorry aber irgendwie sieht das nach einen kompletten Mischmasch von C und C++ aus... grauenhaft<<

Welche Codeteile meinst Du damit genau?

>>ne Idee was überhaupt in _data drin steht zu dem Zeitpunkt wenn es crasht??<<

Nachdem ich ein Neues Objekt vom Typ DataPacket erzeuge dürfte im Zeiger_data hoffentlich nichts relevantes drinnstehen.

>>Ich würde auch NIE in einer Methode einen übergebenen Pointer deleten.<<

_data ist eine Membervariable der Klasse DataPacket. Das delete wird im Konstruktor aufgerufen.

>>Und ein GLOBALES OBJEKT in einem KONSTRUKTOR initialisieren. Du weißt schon was passieren kann, wenn du mehrere Objekte erstellst, und jedesmal dein globales Objekt neu instanziiert wird? Alte Daten futsch.

Wenn globales Objekt, dann bitte brav im zugehörigen *.cpp initialisieren, oder besser, wenn du darauf bestehst dass nur ein Objekt der Klasse existieren darf, per Singleton realisieren.<<

Welches Objekt meinst du mit dem Globalen??
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Darwin hat geschrieben:Welches Objekt meinst du mit dem Globalen??
Darwin hat geschrieben:FirstDataPacket wird im Header angelegt, gleich nach der Deklaration des Structs (siehe oben). Die initialisierung erfolgt im Konstruktor von MQueue (siehe oben)
Sobald ein Objekt nicht in nem struct, ner Klasse oder sonst einem Codeblock deklariert ist, ist es global, wenns in nem Header steht kann man aus JEDER Datei, die diesen Header includiert, auf GENAU DIESES Objekt zugreifen.
Darwin
Beiträge: 12
Registriert: 1. Juni 2009 12:56

Beitrag von Darwin »

Danke für die Information.

Wo ist deiner Meinung nach ein guter Platz zum anlegen der Objekte?

Ich habs im Konstruktor versucht, dann kennt er firstDPLink aber in dem Methoden nicht mehr.

Zwecks zugriff: firstDPLink ist Private und nicht static. Wie soll man da darauf zugreifen können?
Antworten