[C++] Probleme mit der Delete-Funktion

Torhe

Lt. Junior Grade
Registriert
Nov. 2006
Beiträge
392
Hi,

als hausübung haben wir eine kleine übungsaufgabe zum thema flexible variblen und arrays. die übungsaufgabe ist, ein programm zu schreiben, welches immer nach "noch eine Zahl" fragt, und anschließen wenn die antwort "j" eingegeben wird, noch eine zahl inzufügt, bei "n" alle bisher eingegebnen zahlen ausgibt.

damit man das mit den flexiblen arrays übt, geht es darum dass immer nur ein "1 größeres" array neu erstellt wird und dann das alte array wird rüberkopiert. nach jedem rüberkopieren muss das alte array wieder gelöscht werden.

wenn ich die 2 "delete" auskommentiere funnktionieres ohne probleme. wenn ich es im quell code drinen haben, funktioniert gar nichts mehr bzw. das programm stürtzt ab.

ich hoffe ihr könnt mir helfen :)

Code:
//FlexArr_main.cpp
//Flexibles Array. wächst mit dem Bedarf mit
//Benutzer wird gefragt, ob er noch eine Zahl eingeben will
//mit Hilfe von 2 dynamischen Arrays werden alle Zahlen in EIN Array gespeichert

#include <iostream>
using namespace std;

int main ()
{

char weiter='j';
int Anzahl=1;
cout<<"noch eine Zahl? [j/n] ";
cin>>weiter;
int* pInt_alt=new int[1];
int* pInt_neu=new int[1];
delete[]pInt_alt;
delete[]pInt_neu;

	while (weiter!='n')
	{
		pInt_alt=new int[Anzahl-1];
		pInt_alt=pInt_neu;
		
		//delete[]pInt_neu;
		
		Anzahl++;
		pInt_neu=new int[Anzahl];
		for (int i=0;i<(Anzahl-1);i++)
		{
			pInt_neu[i]=pInt_alt[i];
		}
		cout<<Anzahl-1<<". Zahl: ";
		cin>>pInt_neu[Anzahl-1];
		
		//delete[]pInt_alt;
		
		cout<<"noch eine Zahl? [j/n] ";
		cin>>weiter;
	}

cout<<endl<<"Sie haben flogende Zahlen eingegeben: "<<endl;

for (int i=0;i<(Anzahl-1);i++)
{
	cout<<i+1<<". Zahl: "<<pInt_neu[i+1]<<endl;
}

return 0;
}
 
Ähm... was machst du denn hier? "pInt_alt=pInt_neu;" Du hast deinen pInt_neu doch knapp überhalb der while-Schleife gelöscht. Dann ist's ja auch klar, dass ein "delete []" dadrauf nicht funktioniert.
 
das muss nichts ausmachen. "delete" bedeutet ja nur "speicherplatz wieder freigeben"
und das verwunderliche ist, dass das programm deinen error bringt, wenn ich die 3. zahl eingebe. ist daher beim "delete[]pInt_alt" aber erst bei der 3. zahl :freak:

Die Fehlermeldung:

Code:
Debug Assertion Failed!

Program: ...
File: dbgdel.cpp
Line: 52

Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

For information on how your program can cause an assertion failure, see the Visual C++ documention on asserts.

(Press Retry to debug the application)
 
Genau, "delete []" bedeutet, dass Speicherplatz wieder freigegeben werden soll. Wenn an der Stelle im Speicher allerdings nichts sitzt, (weils eben schon gelöscht wurde) dann gibt's 'nen Speicherfehler.

Um das mal zu untermauern: :D

Code:
int main() {
	int *ptr = new int[10];
	delete [] ptr;
	//delete [] ptr;
}

Daraus ergibt sich auch die Existenzberechtigung für sog. "intelligente Zeiger" (smart pointers).

"pInt_alt=pInt_neu;" ist übrigens eine sogenannte "flache Kopie". Das heißt, es wird nur die Speicherstelle kopiert. (Logisch, da beides Zeiger auf int-Arrays sind.) Eine vollständige Kopie würdest du bspw. mit "while((*pInt_alt++=*pInt_neu++));" machen. (Das lässt sich natürlich auch noch deutlich lesbarer als for-Schleife schreiben.)
 
Zuletzt bearbeitet:
ok gut zu wissen. aber warum reicht es nicht aus, die "adresse" zu kopieren? (ist eigentlich von mir so gewollt)

aber ich versteh nicht warum es diesen error gibt. wenn das program das erste mal durchläuft, gibt kommen ja auch 2 delete's hintereinander (von mir zum austesten gemacht). aber warum er genau beim 3. mal durchgehen beim "unteren" delete aussteig ist mir ein rätsel. (ab ich ausprobiert mit immer cout's an allen möglichen stellen)

allerdings wenn ich 2 zahlen eingebe und anschließen auf "nein" gehe bekomme ich als ausgabe nur "-572662307" (dürfte wohl eine leere speicheradresse sein...)

also muss ich doch eine richtige kopie machen, wie moagnus es geschrieben hat?
nur leider versteh ich diese while schleife nicht wirklich... wäre es möglich die "for" version davon zu posten?
 
Wenn du deine for-Schleife um den Deferenzierungsoperator '*' erweiterst, hast du genau dasselbe. Wenn du allerdings nur die Adresse kopierst und anschließend mit einem "delete []" den Inhalt an dieser Adresse löschst, ist der zweite Link dazu natürlich auch nutzlos geworden.

Bei mir funktioniert das Programm übrigens tadellos. Aber arbeite doch mal ein bisschen an der richtigen Schreibweise und ein bisschen übersichtlicherem Code. ;)
 
durch logisches überlegen würde ich sagen die for schleife müsste so aussehen:

Code:
{
pInt_neu[i]=*pInt_alt[i];
}

ich will ja dass an pInt_neu an der stelle der WERT (*) von pInt_alt an der stelle bekommt.

dabei bekomm ich aber einen error (unglültige deferenzierung) das würde heiße ich darf ich darf darauf "nicht zugreifen"
 
Das sind zweierlei Schuhe:
pInt_neu enthält einen Zeiger auf den Speicherbereich.
Wenn du noch ein Sternchen davorsetzt, klappt's ;)
 
irgendwie nicht :freak:

Code:
		for (int i=0;i<(Anzahl-1);i++)
		{
			*pInt_neu[i]=*pInt_alt[i];
		}

und ich bekomme 2 errors (beim kompilieren) error C2100: Ungültige Dereferenzierung.


Hab ich das jetzt richtig verstanden: ein sternchen bedeutet dass ich auf den wert zugreife, welcher sich hinter der adresse verbirgt.
 
Jepp, genau so ists.

Ich hätte dir ja 'ne ausführliche Version geschrieben, aber als ich meinen Beitrag abschicken wollte, war die Session-ID irgendwie abgelaufen und mein Beitrag leider im Eimer. Deswegen warn die letzten Beiträge so kurz...

"Ungültige Dereferenzierung" bedeutet, dass du den Speicherbereich eines Arrays überschritten hast und auf ungültige Stellen zugreifst, die entweder komplett außerhalb des Speicherbereiches liegen, oder du greifst versehentlich auf das terminierende Nullbyte zu.
 
ist natürlich ärgerlich dass du deinen schönen text verlohren hast, aber bei firefox hättest du auf zurück klicken können und es hätte dir den text nach mal angezeigt. dann STRG-A und STRG-C und eine neue antwort erstellen klicken und dann STRG-V ;)


@topic:
dann müsste es doch so funktionieren.

Code:
//FlexArr_main.cpp
//Flexibles Array. wächst mit dem Bedarf mit
//Benutzer wird gefragt, ob er noch eine Zahl eingeben will
//mit Hilfe von 2 dynamischen Arrays werden alle Zahlen in EIN Array gespeichert

#include <iostream>
using namespace std;

int main ()
{

char weiter='j';
int Anzahl=1;
cout<<"noch eine Zahl? [j/n] ";
cin>>weiter;
int *pInt_alt=0;
int *pInt_neu=new int[1];

	while (weiter!='n')
	{
		pInt_alt=new int[Anzahl-1];
		pInt_alt=pInt_neu;
		
		delete[]pInt_neu;
		
		Anzahl++;
		pInt_neu=new int[Anzahl];
		for (int i=0;i<(Anzahl-1);i++)
		{
			*pInt_neu[i]=*pInt_alt[i];
		}
		cout<<Anzahl-1<<". Zahl: ";
		cin>>pInt_neu[Anzahl-1];
		
		delete[]pInt_alt;
		
		cout<<"noch eine Zahl? [j/n] ";
		cin>>weiter;
	}

cout<<endl<<"Sie haben flogende Zahlen eingegeben: "<<endl;

for (int i=0;i<(Anzahl-1);i++)
{
	cout<<i+1<<". Zahl: "<<*pInt_neu[i+1]<<endl;
}

return 0;
}

nur leider bei den sternchen bekomme ich diese errors die 3 sternchen in beiden for-schleifen.
 
Zuletzt bearbeitet:
Hab mir jetzt mal deinen Code komplett durchgelesen. :rolleyes:
Meine Lösung:
Code:
#include <iostream>
using namespace std;

int main() {
	int *ptr1, *ptr2;
	char weiter;
	int eingabe;
	unsigned int anzahl=0;
	
	do {
		cout<<"Wollen Sie (noch) eine Zahl eingeben? (J/N)"<<endl;
		cin>>weiter;
		
		if(weiter=='n' || weiter=='N') { break; }
		
		if(weiter=='j' || weiter=='J') {
			if(anzahl==0) {
				cin>>eingabe;
				ptr1 = new int[++anzahl];
				ptr1[anzahl-1] = eingabe;
			} else {
				ptr2 = new int[++anzahl];
				for(int i=0; i<anzahl-1; ++i) {ptr2[i]=ptr1[i];}
				delete [] ptr1;
				cin>>eingabe;
				ptr2[anzahl-1] = eingabe;
				ptr1 = new int[anzahl];
				for(int i=0; i<anzahl; ++i) {ptr1[i]=ptr2[i];}
				delete [] ptr2;
			}
		}
			
	} while(weiter!='n' || weiter!='N');

	for(int i=0; i<anzahl; ++i) {
		cout<<ptr1[i]<<endl;
	}
}

Zu den while-Konstrukten: Sorry, hab dich da vllt. ein wenig auf die falsche Fährte gelockt. Wenn die Arrays unterschiedlich groß sind, ist das nicht so ganz das Wahre. Benutz also lieber die for-Schleifen, das ist auch deutlich lesbarer. ;)

Edit: Zu den Fehlern: Sorry, ich hab oben Krampf geschrieben. War wohl geistig ein bisschen abwesend...
Es gibt generell zwei Möglichkeiten auf ein Array-Element zuzugreifen.
1) Zeigerarithmetik: for (int i=0;i<(Anzahl-1);i++) { *(pInt_neu+i)=*(pInt_alt+i); } funktioniert, da pInt_neu ein Zeiger auf das erste Element im Array ist.
2) Über den Indexoperator: for (int i=0;i<(Anzahl-1);i++) { pInt_neu=pInt_alt; }
Der funktioniert dann allerdings ohne die Sternchen. Damit ist die Handhabung von dynamisch erzeugten Arrays nicht anders als von Solchen, die auf dem Stack gespeichert sind. Sorry mein Fehler...

Hoffe, deine Fragen haben sich geklärt. Ansonsten nicht zögern. Du hast noch was gut bei mir wegen dem Krampf, den ich dir erzählt hab

Edit: Wo ich mir noch nicht ganz im Klaren bin, ist der Teil hier: "mit Hilfe von 2 dynamischen Arrays werden alle Zahlen in EIN Array gespeichert". Das klingt mehr nach drei Arrays. Naja, die Funktionalität wird ja erfüllt und wahrscheinlich sind wir schon den umständlichsten Weg gegangen...
 
Zuletzt bearbeitet:
vielen dank für deine mithilfe :)
ich habe dein programm studiert, dann deine im prinzip übernommen und anschließend nochmal eines komplett neu geschrieben. und es funktioniert auch.

Code:
//FlexArrReload_2_main.cpp
//Flexibles Array. wächst mit dem Bedarf mit
//Benutzer wird gefragt, ob er noch eine Zahl eingeben will
//mit Hilfe von 2 dynamischen Arrays werden alle Zahlen in EIN Array gespeichert

#include <iostream>
using namespace std;

int main ()
{
char weiter='j';
int Anzahl=0, Zahl=0;
int *pInt_1=0, *pInt_2=0;

cout<<"noch eine Zahl? [j/n] ";
cin>>weiter;

while (weiter!='n')
{
	if (Anzahl==0)
	{
		cout<<Anzahl+1<<". Zahl: ";
		cin>>Zahl;

		pInt_2=new int[Anzahl+1];
		pInt_2[Anzahl]=Zahl;

		Anzahl++;
	}

	else
	{
		cout<<Anzahl+1<<". Zahl: ";
		cin>>Zahl;

		pInt_1=new int[Anzahl+1];
		for (int i=0;i<=(Anzahl-1);i++)pInt_1[i]=pInt_2[i];
		pInt_1[Anzahl]=Zahl;

		delete [] pInt_2;
		pInt_2=new int[Anzahl+1];
	
		for (int i=0;i<=(Anzahl);i++)pInt_2[i]=pInt_1[i];
		Anzahl++;
	}
	cout<<"noch eine Zahl? [j/n] ";
	cin>>weiter;
	
}
cout<<endl<<"Sie haben flogende Zahlen eingegeben: "<<endl;

for (int i=0;i<(Anzahl);i++)cout<<i+1<<". Zahl: "<<pInt_2[i]<<endl;

return 0;
}

was mich noch interessieren würde: ist der code "gut lesbar" und wie kann ich eine code "einfach lesbar" machen?
 
Einige Tipps für lesbareren und damit auch leichter verständlicheren Code:
1. Es empfiehlt sich, den Funktionskörper mit einem Tabulator einzurücken. Damit sieht man auf einen Blick, was zur Funktion gehört und was nicht.
2. Konstrukte, wie diese while-Schleifen mit Zeigern, die nach jedem Durchlauf erhöht werden, sind verdammt unlesbar und führen zu Fehlern. Es ist ein Fehler zu denken, dass bspw. eine for-Schleife, die dieselbe Funktion wie das while-Konstrukt hat, das Programm verlangsamt, nur weil der Code länger ist. Denn erstens optimiert der Compiler bereits kräftig (evtl. mal die Dokumentation zu Rate ziehen für die verschiedenen Optionen/Optimierungsstufen) und zweitens ist in den allermeisten Fällen wichtiger, dass der Code fehlerfrei ist und keine unerwünschten Nebeneffekte produziert, als dass er kompakt ist.
3. Ein weiterer wichtiger Punkt sind aussagekräftige Bezeichner. Variablen ptr2 zu taufen mag hier in Ordnung sein, spätestens bei umfangreicheren Programmen wird es aber lästig ständig zur Deklaration zurückzublättern, um die Funktion der Variablen nachzuschlagen. Was sich hier noch hinzufügen lässt: Es ist gängig, Variablennamen stets kleinzuschreiben. Um mehr Informationen jedoch in den Namen zu packen, kannst du entweder die "Kamelhöcker" benutzen (bspw. "int ersteIntVariableImProgramm;", also sprich Großbuchstaben im Namen verwenden) oder du entscheidest dich für Unterstriche im Bezeichner ("int erste_int_variable;"). Das Ganze ist Geschmackssache und die Entscheidung liegt bei dir, sollte dann aber auch konsequent durchgezogen werden; ansonsten ist es ein wenig störend und verwirrend.
4. Kommentare helfen ungemein beim Verständnis. Kommentiert werden sollte jedoch nur, wo in einer Anweisung mindestens zwei Gedanken stecken, also nicht jede Variablendeklaration.

Zusammengefasst: Immer ordentlich einrücken (Tabulatoren schaffen gleiche Abstände und sind wesentlich einfacher zu handhaben als Leerzeichen.) bei den Strukturen (auch bei if, while etc.) und Funktionen und ein paar eingestreute Kommentare sollten die Lesbarkeit doch schon deutlich steigern.

Zu deinem Code: Ich würde bei der for-Schleife am Ende noch Klammern setzen, da du die bei Erweiterungen wahrscheinlich vergisst. Wie schon gesagt... nimmt vllt. ein bisschen Platz weg, aber macht nichts langsamer. Alternativ kannst du natürlich auch den Schleifenkörper mit 'nem Tab einrücken.
Das "return 0;" ist übrigens nicht zwingend notwendig, stört aber auch nicht.
 
Zurück
Oben