PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Fehlermeldung bei Programmende


Einfachkrank
2005-01-14, 14:15:00
Moin,

Folgender Fehler kommt tritt häufig bei Programmen auf, die ich geschrieben habe. Ich hab das im Debugger mal mitverfolgt und es passiert genau am Ende der WinMain() beim Verlassen durch return...
Ich programmiere mit Visual C++ 6.0 Standard, WinXP und verwende eigene DLL's.
Komischerweise tritt dieser Fehler auch nur auf, wenn ich das Programm im Debug Modus erstellt habe und nicht im Release...
http://www.michael-eberhardt.de/Files/error.jpg

Mir passiert es auch oft, dass wenn ich über Pointer Speicher allokiert habe und ihn am Programmende wieder freigeben will, dass dort sich das Programm mit einer Speicherzugriffsverletzung verabschiedet.

Hat da jemand ein paar Ratschläge oder Hinweise, auf was ich besonders achten sollte, für mich?

MFG Einfachkrank

zeckensack
2005-01-14, 15:42:57
Du hast ein Speicherleck. Entweder hast du Speicher angefordert aber nicht wieder freigegeben, oder mehrfach freigegeben.

Tips:
1)Alle Zeiger im Programm mit NULL initialisieren. Dh alle globalen Zeiger:
void* was_bin_ich=NULL;
... und alle Klassenmember die Zeiger sind (im Konstruktor).

2)Bevor du Speicher freigibst, erstmal prüfen ob der entsprechende Zeiger NULL ist. Nur freigeben, wenn nicht, und dann den Zeiger auf NULL setzen.
Makros:#define SAFE_FREE(a) if (NULL!=a) {free(a);a=NULL;}
#define SAFE_DELETE(a) if (NULL!=a) {delete a;a=NULL;}
#define SAFE_ARRAY_DELETE(a) if (NULL!=a) {delete[] a;a=NULL;}

3)Bevor du Speicher anforderst, prüfen ob der Zeiger, in den du die Adresse speichern willst, NULL ist.
Statt:
blub=malloc(12345);
dies:
assert(NULL==blub);
blub=malloc(12345);

e: #3 funktioniert nur im Debug-Build. assert feuert einen Fehler, wenn der Ausdruck in den Klammern nicht wahr ist. Auf die Weise kannst du dann den Debugger aufrufen, und sehen welcher Zeiger überschrieben werden sollte, und das Problem hoffentlich leichter aufspüren.

micki
2005-01-14, 16:18:50
kann unter umständen auch daran liegen, wenn man beim ableiten von klassen keinen virtuellen destruktor benutzt.



anstatt des SAFE_... wäre es auch möglich ein template für pointer zu benutzen, neben dem SAFE_... kram denn du dort implementieren könntest, könntest du in deren destructor auch einbauen, dass die templates prüfen, ob die objekte 'freigegeben' wurden (mittels assert).
zur not müßte man auch refcounting einbauen usw.

in boost gibt es das alles auch schon fertig, zumindestens im debug (falls man performancebeführtungen hat) sollte man auto-/smartpointer verwenden.

MfG
micki

Einfachkrank
2005-01-14, 20:53:49
#define SAFE_FREE(a) if (NULL!=a) {free(a);a=NULL;}
#define SAFE_DELETE(a) if (NULL!=a) {delete a;a=NULL;}
#define SAFE_ARRAY_DELETE(a) if (NULL!=a) {delete[] a;a=NULL;}
Wo ist der Unterschied zwischen free() und delete?
Und wie siehts genau mit Löschen von Arrays aus. weil du delete[] a geschrieben hast? Ich habe das bisher immer so gemacht:

int *bla;
bla = new int[5];
// ...
delete bla;

Cast22
2005-01-14, 22:46:07
jedes modul (exe, dlls) gegen die multithreaded rumtime library dll linken

micki
2005-01-15, 01:12:00
Wo ist der Unterschied zwischen free() und delete?
Und wie siehts genau mit Löschen von Arrays aus. weil du delete[] a geschrieben hast? Ich habe das bisher immer so gemacht:

int *bla;
bla = new int[5];
// ...
delete bla;


du mußt mit delete[] löschen wenn du nen arraypointer hast, weil sonst nicht die destruktoren aller elemente eines arrays aufgerufen werden.

MfG
micki

zeckensack
2005-01-15, 10:06:53
Wo ist der Unterschied zwischen free() und delete?free gibt Speicher frei, der mit malloc (oder calloc) angefordert wurde.

delete gibt Speicher frei, der mit new angefordert wurde.
du mußt mit delete[] löschen wenn du nen arraypointer hast, weil sonst nicht die destruktoren aller elemente eines arrays aufgerufen werden.Exakt =)

Und wie siehts genau mit Löschen von Arrays aus. weil du delete[] a geschrieben hast? Ich habe das bisher immer so gemacht:

int *bla;
bla = new int[5];
// ...
delete bla;
Das ist böse. Gewöhne dir die Variante mit den [] an. Bei primitiven Typen (wie int) ist das zwar nicht soo schlimm, bei Klasseninstanzen wird's dafür umso gefährlicher.

Klassisches Speicherleck in C++:
class
NestedClass
{
public:
NestedClass():dummy(0){}
private:
int dummy;
};

class
Class
{
public:
Class(){nested0=new NestedClass; nested1=new NestedClass;}
~Class(){delete nested0;delete nested1;}
private:
NestedClass* nested0;
};

int
main()
{
Class* object=new Class[2];
delete object;
return(0);
}Nach main liegen noch zwei NestedClass-Instanzen auf dem Heap herum, weil nur der Destruktor von object[0] aufgerufen wurde, nicht aber der Destruktor von object[1].
Darüberhinaus ist object[1] nicht mehr gültig (weil der Basiszeiger object nicht mehr gültig ist). Dh die beiden fehlenden NestedClass-Instanzen sind somit auch nicht mehr auffindbar, und können nicht mehr gelöscht werden.

Coda
2005-01-15, 11:03:38
#define SAFE_FREE(a) if (NULL!=a) {free(a);a=NULL;}
#define SAFE_DELETE(a) if (NULL!=a) {delete a;a=NULL;}
#define SAFE_ARRAY_DELETE(a) if (NULL!=a) {delete[] a;a=NULL;}Zeckensack ich will dir ja nicht zu nahe treten, aber ein delete oder free mit Argument NULL/0 macht per Definition nichts. Das Macro ist absolut unnötig, zumindest das "if".

Und in C++ ist es 0 und nicht NULL. Außerdem wäre das ganze auch ohne Macros gegangen, was immer zu bevorzugen ist.

zeckensack
2005-01-15, 11:51:49
Zeckensack ich will dir ja nicht zu nahe treten, aber ein delete oder free mit Argument NULL/0 macht per Definition nichts. Das Macro ist absolut unnötig, zumindest das "if".Ja, das habe ich auch schonmal gelesen. Für das free ist der Test aber nötig.
Je nachdem was man für einen verranzten Compiler hat, braucht man das auch für C++ (uralte Watcom und Borland C++ Builder-Versionen IIRC). Aber du hast natürlich Recht. Mit auch nur halbwegs aktuellen Compilern ist das unnötig.
Und in C++ ist es 0 und nicht NULL.Das ist mir relativ egal. NULL nehme ich für Zeiger, 0 für "normale" Integer. IMO erhöht das die Lesbarkeit. Alle C++-Compiler verstehen und akzeptieren NULL.
*schulterzuck*
Außerdem wäre das ganze auch ohne Macros gegangen, was immer zu bevorzugen ist.Das Makro ist nicht unnötig, denn es garantiert, dass der Zeiger danach NULL (oder von mir aus auch 0) ist, und nicht mehr auf den freigegebenen Speicher zeigt. Das ist der eigentliche Zweck der Übung.

edit: Oder schlägst du statt einem Makro eine Template-Funktion dafür vor? :naughty:

Coda
2005-01-15, 11:52:18
Für das free ist der Test aber nötig.Nein, auch dort nicht.
Alle C++-Compiler verstehen und akzeptieren NULL.Nö, du must das Macro irgendwo definiert haben.
Das Makro ist nicht unnötig, denn es garantiert, dass der Zeiger danach NULL (oder von mir aus auch 0) ist, und nicht mehr auf den freigegebenen Speicher zeigt. Das ist der eigentliche Zweck der Übung.Ich meinte damit dass man das ganze auch als Funktion machen kann. Macros sind wirklich nur Notlösungen und schlechter Stil, vor allem in C++.

Einfachkrank
2005-01-15, 13:03:58
Also ich bin mein kleines Mini-game mal durchgegangen und hab auch rausgefunden an welchem Teil des Programm es liegen muss(das Speicherleck). Allerdings bin ich nicht wirklich weiter gekommen, was das Problem an sich betrifft. Hier habe ich die Quellcodedateien die das Ding betreffen... vielleicht könnte einer von euch mal drüber schaun :rolleyes:

Das sind die zwei Dateien aus meiner DLL, wo Models geladen werden etc.
m3d_model.h (http://www.michael-eberhardt.de/Files/leck/m3d_model.h)
m3d_model.cpp (http://www.michael-eberhardt.de/Files/leck/m3d_model.cpp)

Und die zwei Dateien aus dem Mini-game in dem es verwendet wird...
abi_weapon.h (http://www.michael-eberhardt.de/Files/leck/abi_weapon.h)
abi_weapon.cpp (http://www.michael-eberhardt.de/Files/leck/abi_weapon.cpp)

Wichtige Makros dazu sind:

//# define _MDLL __declspec(dllimport)

# ifndef _MDLL
# define _MDLL __declspec(dllexport)
# endif
// und seit neustem :-)
# define pDelete(p) if(p != NULL) { delete p; p = NULL; }
# define pADelete(p) if(p != NULL) { delete[] p; p = NULL; }


Wäre echt super wenn ihr vielleicht mal drüber sehen könntet! *liebguck* :rolleyes:

Coda
2005-01-15, 14:36:29
# define pDelete(p) if(p != NULL) { delete p; p = NULL; }
# define pADelete(p) if(p != NULL) { delete[] p; p = NULL; }Wie gesagt ist das if völlig unnötig und eine template funktion würde das gleiche eleganter machen.

template <class T> void pDelete(T &pointer) { delete pointer; pointer = 0; }
template <class T> void pADelete(T &pointer) { delete[] pointer; pointer = 0; }Macros sollten aus verschiedensten Gründen gemieden werden, wenn sie nicht absolut nötig sind (was sie ganz selten sind).

Wobei ich den Sinn der Funktion immer noch nicht verstehe. Man löscht einen pointer eigentlich von vornherein nur einmal :rolleyes:

micki
2005-01-15, 16:14:33
Wobei ich den Sinn der Funktion immer noch nicht verstehe. Man löscht einen pointer eigentlich von vornherein nur einmal :rolleyes:
tja, und man löscht an sich auch jeden speicher wieder, den man angefordert hat.

aber damit mit keine flüchtigkeitsfehler hat, benutzt man eben helper, die das garantieren, dass ein speicher nur einmal gelöscht wird.

wobei ich immer noch für etwas wirklich sicheres wäre wie die boost smartpointer.

MfG
micki

Coda
2005-01-15, 16:28:28
wobei ich immer noch für etwas wirklich sicheres wäre wie die boost smartpointer.Ack! Am besten in Verbindung mit STL Containern.

Und der absolute Geheimtipp ist eh Paul Nettle's MemoryManager. Da kannst du gar keine Speicherfehler mehr machen :)

http://www.fluidstudios.com/pub/FluidStudios/MemoryManagers/Fluid_Studios_Memory_Manager.zip

micki
2005-01-15, 17:09:43
Ack! Am besten in Verbindung mit STL Containern.

Und der absolute Geheimtipp ist eh Paul Nettle's MemoryManager. Da kannst du gar keine Speicherfehler mehr machen :)

http://www.fluidstudios.com/pub/FluidStudios/MemoryManagers/Fluid_Studios_Memory_Manager.zip

ask midnight rulezz :D

Coda
2005-01-15, 17:37:22
Rrrrrrrrrrrrrrrrrichtig :)

Einfachkrank
2005-01-15, 20:17:57
Da hab ich gleich noch eine Frage. Wo ist der Unterschied zwischen denen beiden Funktionsköpfen?
void test(int value);
void test(int &value);
Was hat der Adressoperator hier für eine Bedeutung?

deleter
2005-01-15, 20:18:45
Na ja, wer diese SafeDelete Makro/Funktion benutzt versteckt sich leicht Logikfehler.

deleter
2005-01-15, 20:19:55
Da hab ich gleich noch eine Frage. Wo ist der Unterschied zwischen denen beiden Funktionsköpfen?
void test(int value);
void test(int &value);
Was hat der Adressoperator hier für eine Bedeutung?

Du kennst Referenzen nicht und programmierst schon Spiele?

Demirug
2005-01-15, 20:24:34
Da hab ich gleich noch eine Frage. Wo ist der Unterschied zwischen denen beiden Funktionsköpfen?
void test(int value);
void test(int &value);
Was hat der Adressoperator hier für eine Bedeutung?

Damit wird der Wert als Referenz übergeben. Funktional führt das dazu das am Ende der Methode der Wert wieder an den Aufrufer zurück gegeben wird. Das ganze ist ähnlich der übergabe via Zeiger hat aber den Vorteil das man in der Methode nicht prüfen muss ob man einen gültigen Zeiger bekommen hat.

Demirug
2005-01-15, 20:26:22
Du kennst Referenzen nicht und programmierst schon Spiele?

Das ist doch kein Hinderniss. Wenn ich da an den Code von meinem ersten verkaufen Spiel denke ...

GloomY
2005-01-15, 23:21:10
Sorry wegen Offtopic, aber wir sind ja eh schon von der eigentlichen Frage abgekommen:Damit wird der Wert als Referenz übergeben. Funktional führt das dazu das am Ende der Methode der Wert wieder an den Aufrufer zurück gegeben wird. Das ganze ist ähnlich der übergabe via Zeiger hat aber den Vorteil das man in der Methode nicht prüfen muss ob man einen gültigen Zeiger bekommen hat.Ist das das Pascal-artige "Call-by-Value-Return"? Gibt's das in C (Ich meine damit reines C)? Oder ist das eine Neuerung in C++?

Demirug
2005-01-15, 23:33:35
Sorry wegen Offtopic, aber wir sind ja eh schon von der eigentlichen Frage abgekommen:Ist das das Pascal-artige "Call-by-Value-Return"? Gibt's das in C (Ich meine damit reines C)? Oder ist das eine Neuerung in C++?

Nein, das ist ganz normales "Call-by-reference". Theoretisch darf der Compiler das aber auch als "Call-by-Value-Return" behandeln. Es muss eben nur sicher gestellt sein das nach dem verlassen der Methode die ursprünglich übergebene Variable alle Veränderungen mitgemacht hat. Bei richtigen Objekten macht es da natürlich keinen Sinn diese zweimal zu kopieren. Bei einfachen Typen kann es aber durchaus sinn machen diese für die Laufzeit einer Methode in ein Register zu kopieren sie dort zu halten und am Ende wieder zurück zu kopieren.

Bei C muss man das alles mit Zeigern machen.

Einfachkrank
2005-01-16, 00:23:37
Ja, doch Referenzen kenn ich schon... nur ich hab außer in Delphi dass noch nie verwendet, denn in C++ arbeite ich immer mit Zeigern...

Das Problem hat sich übrigens erledigt, denn seit ich mein Projekt auf Multithreaded-DLL ist das Speicherleck verschwunden :rolleyes:

Aber trotzdem DANKE!!!

Unknow
2005-01-16, 05:06:17
Na ja, so richtig C++ programmierst du ja nicht. Eher so ein C/C++ Misch Masch. Aber wenns dir gefällt...

Einfachkrank
2005-01-16, 20:46:54
Na ja, so richtig C++ programmierst du ja nicht. Eher so ein C/C++ Misch Masch. Aber wenns dir gefällt...
Ja ich weiß. Sollte man das lassen oder macht das keinen Unterschied?

Coda
2005-01-16, 22:52:02
Sollte man eigentlich lassen, reines C++ ist normalerweiße "besserer" Code.

Demirug
2005-01-16, 23:02:00
Sollte man eigentlich lassen, reines C++ ist normalerweiße "besserer" Code.

Ja, wobei sich die Trennline da nicht immer ganz sauber ziehen lässt. Vorallem da viele APIs immer noch C APIs sind. Richtig übel wird es wenn dann noch COM ins Spiel kommt. Hatte da mal den Fall wo ich von einem COM Objekt ein C++ Objekt ansprechen musste das eine C API gekapselt hat.

Exxtreme
2005-01-17, 10:41:39
Na ja, so richtig C++ programmierst du ja nicht. Eher so ein C/C++ Misch Masch. Aber wenns dir gefällt...
Unter Windows wirst du immer Mischmasch haben wenn du ein "Praktiker" bist. :) Keine Klassenbibliothek kapselt das Win32-API komplett und du wirst kaum um einige direkte Win32-Aufrufe rumkommen können.

Coda
2005-01-17, 13:23:52
Keine Klassenbibliothek kapselt das Win32-API komplett und du wirst kaum um einige direkte Win32-Aufrufe rumkommen können.Qt und wxWidgets tun das eigentlich recht gut :rolleyes:

Exxtreme
2005-01-17, 13:25:28
Qt und wxWidgets tun das eigentlich recht gut :rolleyes:
Recht gut != 100%. :)