printf("Kann keinen Speicher mehr reservieren!\n");
29
returnEXIT_FAILURE;
30
}
31
for(i=0;i<size;i++)
32
temp[i]=value[i];
33
size+=more;
34
value=malloc(size*sizeof(int));
35
if(NULL==value){
36
printf("Kann keinen Speicher mehr reservieren!\n");
37
returnEXIT_SUCCESS;
38
}
39
for(i=0;i<size;i++)
40
value[i]=temp[i];
41
}while(more!=0);
42
printf("Hier Ihre Werte\n");
43
for(i=0;i<size;i++)
44
printf("value[%d] = %d\n",i,value[i]);
45
returnEXIT_SUCCESS;
46
}
In der Zeile value = malloc(size * sizeof(int)); (nach size+more) wird
neuer Speicher angefordert. Die Werte aus value wurden in temp
zwischengespeichert. value zeigt doch aber schon auf einen
Speicherbereich. Müsste ich diesen nicht vorher freigeben??
D.h.
1
size+=more;
2
free(value);
3
value=malloc(size*sizeof(int));
Temp muss auch noch freigeben werden, aber da ist die Sachlage klar.
Vielen Dank.
Am2302 schrieb:> Müsste ich diesen nicht vorher freigeben??
Ja, ausser du willst ein Speicherleck.
Am2302 schrieb:> value = (int *)malloc(size*sizeof(int));
Den Cast nach int * sollte man auch weglassen, der ist unnötig.
Ausserdem bevorzuge ich folgende Schreibweise:
1
int*Var=malloc(Size*sizeof(*Var));
So kann sich der Datentyp von Var ändern, ohne dass ich alle sizeof in
den malloc-Aufrufe anpassen muss.
Im Allgemeinen sieht das Programm eher unübersichtlich aus. Ich würde da
einige Dinge in Funktionen verpacken. Zudem noch Klammern um alle
Schleifen und Variablen erst Deklarieren, wenn sie benötigt werden
anstatt alle am Anfang der Funktion. Die Funktion scanf ist auch noch
mal ein Thema für sich, ich würde diese nicht verwenden.
Am2302 schrieb:>
(http://openbook.rheinwerk-verlag.de/c_von_a_bis_z/014_c_dyn_speicherverwaltung_007.htm)
Ich rate von diesem Lehrwerk ab. Ich habe Teile des Buches gelesen und
da sind teilweise üble Fehler drin. Ausserdem ist der Programmierstil,
wie man bei diesem Beispiel gut sieht, eher schlecht als recht und es
werden viele wichtige Details ignoriert, da es "normalerweise" auch so
funktioniert.
Ja, das müsste rein.
Allerdings ist der ganze Konstrukt so etwas ungünstig.
Du hast doch schon am Anfang abgefragt, wieviele Werte benötigt werden.
Danach soll dennoch erweitert werden können?
Naja, wie auch immer, eigentlich vermeidet man so ein Konstrukt, da
dadurch der Speicher fragmentiert wird. Aber wenn es denn sein muss,
würde ich es in dieser Art machen:
temp = malloc(newsize*sizeof(int));
for(i=0; i<size; i++) {
temp[i] = value[i];
}
free(value);
value = temp;
size = newsize;
Dadurch sparst Du einen Kopiervorgang und ein malloc/free Pärchen.
EDIT: Wenn diese Code aus einem Lehrbuch ist, solltest Du ein anderes
nehmen. So wird das niemand programmieren in der Praxis.
Markus M. schrieb:> EDIT: Wenn diese Code aus einem Lehrbuch ist, solltest Du ein anderes> nehmen. So wird das niemand programmieren in der Praxis.
Cool. Danke für die Antworten.
Der Speicher wird fragmentiert. Verstanden. Könntest du noch anreißen
wie man es richtig macht?
Eine Empfehlung für ein C Buch nehme ich auch gerne. :-)
Einen schönen Abend.
Am2302 schrieb:> Könntest du noch anreißen wie man es richtig macht?
Den Anfang hat Markus bereits geliefert, damit ersparst du dir unnötige
Kopiererei oder du verwendest statt malloc realloc (das erste Mal
benötigst du malloc), dann werden deine Daten automatisch kopiert.
Ausserdem gleich noch scanf durch fgets ersetzen. Siehe z.B. hier
(Anfangs gehts um was anderes, einfach weiterlesen):
Beitrag "Wann Tastaturpuffer mit fflush(stdin); löschen?"Am2302 schrieb:> Eine Empfehlung für ein C Buch nehme ich auch gerne. :-)
The C Programming Language von K&R, 2. Auflage. Behandelt zwar noch das
alte C89, aber die späteren Erweiterungen sind im Internet schnell
recherchiert, es sind ja nicht so viele.
Als Nachschlagewerk für die Standardbibliothek empfehle ich
http://www.cplusplus.com/reference/clibrary/
Ist zwar eine Referenz für C++, aber die C Header sind auch mit drin.
Markus M. schrieb:> temp = malloc(newsize*sizeof(int));
Müsste es hier nicht sowieso int* heißen ...
be s. schrieb:> Den Anfang hat Markus bereits geliefert, damit ersparst du dir unnötige> Kopiererei oder du verwendest statt malloc realloc (das erste Mal> benötigst du malloc), dann werden deine Daten automatisch kopiert.
Okay. Danke. realloc gibts bei meinem verwendeten Betriebssystem nicht.
Aber der scheint ja auch nur kopieren und somit den Speicher zu
fragmentieren.
Ich frug nur, da Markus andeutete es geht auch (noch) besser.
be s. schrieb:> Ausserdem gleich noch scanf durch fgets ersetzen. Siehe z.B. hier> (Anfangs gehts um was anderes, einfach weiterlesen):> Beitrag "Wann Tastaturpuffer mit fflush(stdin); löschen?"
Das war nur in dem angegebenen Beispiel und spielt auf meinem µC keine
Rolle :-) Aber trotzdem interessant.
be s. schrieb:> The C Programming Language von K&R, 2. Auflage. Behandelt zwar noch das> alte C89, aber die späteren Erweiterungen sind im Internet schnell> recherchiert, es sind ja nicht so viele.>> Als Nachschlagewerk für die Standardbibliothek empfehle ich> http://www.cplusplus.com/reference/clibrary/> Ist zwar eine Referenz für C++, aber die C Header sind auch mit drin.
Danke, ich glaub das besorge ich mir mal ...
Viele Grüße und noch mals Dank an euch!
be s. schrieb:> das erste Mal benötigst du malloc
Nö, realloc kann auch für das erste Mal verwendet werden. Ist der
übergebene Pointer ein NULL-Pointer, verhält sich realloc wie
malloc.
1
ptr=realloc(NULL,newsize);
Beim Gebrauch von realloc ist es ratsam, nicht so vorzugehen:
1
ptr=realloc(ptr,newsize);
denn dann verwaist im Fehlerfall der bislang angeforderte Speicher, da
der Pointer verworfen wird.
Daher ist immer erst der Rückgabwert auszuwerten, bevor der
ursprüngliche Pointer überschrieben wird:
da ist ausserdem in den Originalroutinen noch ein übler Fehler drinnen
1
temp=malloc(size*sizeof(int));
2
if(NULL==temp){
3
printf("Kann keinen Speicher mehr reservieren!\n");
4
returnEXIT_FAILURE;
5
}
6
for(i=0;i<size;i++)
7
temp[i]=value[i];
8
size+=more;
9
value=malloc(size*sizeof(int));
10
if(NULL==value){
11
printf("Kann keinen Speicher mehr reservieren!\n");
12
returnEXIT_SUCCESS;
13
}
14
for(i=0;i<size;i++)
15
value[i]=temp[i];
und hier zum Schluss wird auf Werte von temp zugegriffen, die überhaupt
nicht existieren.
size ist zu diesem Zeitpunkt schon erhöht worden, value zeigt auch
korrekt auf ein mit dieser Größe allokiertes Array. Aber temp nicht. Das
Array, auf welches temp zeigt hat noch die vorhergehende size.
Man kann es nicht oft genug betonen. "Array out of bounds" Zugriffe sind
die häufigsten und gleichzeitig mit die schlimmsten Fehler in C
Programmen. Hier heisst es sorgfältig arbeiten!
Am2302 schrieb:> Markus M. schrieb:>> temp = malloc(newsize*sizeof(int));>> Müsste es hier nicht sowieso int* heißen ...
Nein. Du willst ja nicht ein Array von Pointern allokieren.
Du willst ein Array von int allokieren. Dort sollen ja Integer drinn
gespeichert werden.
> Okay. Danke. realloc gibts bei meinem verwendeten Betriebssystem nicht.> Aber der scheint ja auch nur kopieren und somit den Speicher zu> fragmentieren.
um Speicherfragmentierung kommst du normalerweise sowieso nicht herum.
Egal ob du mit free freigibst oder nicht, egal ob du mittels realloc
vergrößerst oder nicht. Wenn du allokierten Speicher nicht mittels free
frei gibst, dann hast du ein sog. Speicherloch. Im Englischen sagt man
auch, dein Programm 'leaked' (es leckt).
Speicherfragmentierung ist etwas anderes. Unter Speicherfragmentierung
versteht man die Verwendung des Speichers, so dass zwischen den
allokierten Speicherbereichen Lücken entstehen, die klein genug sind, so
dass man mit ihnen nichts mehr anfangen kann. Dein Programm hat zwar
dann rein rechnerisch zum Beispiel noch 2k frei, aber eben nicht in 1
Stück, sondern in Form von 50 jeweils 4 Byte grossen Blöcken. Rein
rechnerisch könntest du in den noch 2K verbliebenem Speicher also noch
problemlos ein 1K grosses Array unterbringen. Praktisch kannst du das
aber nicht, weil du das benötigte 1K nicht in einem Stück im Speicher
frei hast.
Stell dir die Situation vor:
Das sei dein Speicher von 2000 Byte
rein rechnerisch hast du von deinen 2000 Bytes nur 900 Bytes verbraucht,
hast also 2000 - 900 gleich 1100 Bytes noch frei. In der Grafik siehst
du auch, dass links und rechts von deiner Allokierung noch freier Platz
ist. Trotzdem bringst du in diesem 1100 Bytes freiem Speicher keine
weitere Anforderung mit 800 Bytes unter. Denn die Lücke links vom
allokierten ist 500 Bytes gross und damit kleiner als die geforderten
800 und die Lücke rechts von der Allokierung ist 600 Bytes gross und
damit ebenfalls zu klein. Die Speicheranforderung scheitert, weil dein
Speicher fragmentiert ist.
Die einzige Art das zu vermeiden besteht in C darin, keine dynamischen
Speicheranforderungen zu machen. Das hört sich jetzt schlimmer an als es
ist. Sobald du hinreichend viel Speicher generell zur Verfügung hast,
verschwindet das Problem immer mehr. Aber auf einem kleinen µC mit nur
sehr wenig Speicher ist das ein ernstes Problem. Dort wird es so gelöst,
dass man keine dynamischen Anforderungen macht. Habe ich den Speicher
und ich weiss dass Array1 500 Bytes benötigt, Array 2 braucht 900 Bytes
und Array 3 benötigt 800 Bytes UND weiss ich zusätzlich, dass Array1 und
Array 3 niemals zur selben Zeit benötigt werden, dann lege ich mir
statisch ein Array mit 800 Bytes an, statisch eines mit 900 Bytes und
wickle die Arbeit an dem 500 Byte grossen Array über den 800-er
Speicherblock ab. Der ist zwar größer als benöntigt, aber das stört ja
nicht weiter.
Rufus Τ. F. schrieb:> be s. schrieb:>> das erste Mal benötigst du malloc>> Nö, realloc kann auch für das erste Mal verwendet werden. Ist der> übergebene Pointer ein NULL-Pointer, verhält sich realloc wie> malloc.> ptr = realloc(NULL, newsize);
Danke, das wusste ich noch nicht.
Karl H. schrieb:> Habe ich den Speicher> und ich weiss dass Array1 500 Bytes benötigt, Array 2 braucht 900 Bytes> und Array 3 benötigt 800 Bytes UND weiss ich zusätzlich, dass Array1 und> Array 3 niemals zur selben Zeit benötigt werden, dann lege ich mir> statisch ein Array mit 800 Bytes an, statisch eines mit 900 Bytes und> wickle die Arbeit an dem 500 Byte grossen Array über den 800-er> Speicherblock ab.
Wenn unterschiedliche Datentypen für die Arrays verwendet werden, legt
man beide Arrays in einer union an, eigentlich auch schon nur, wenn man
verschiedene Namen für die Arrays haben möchte. Eine der seltenen
sinnvollen und sicheren Verwendungen einer union.
be s. schrieb:> Wenn unterschiedliche Datentypen für die Arrays verwendet werden, legt> man beide Arrays in einer union an, eigentlich auch schon nur, wenn man> verschiedene Namen für die Arrays haben möchte. Eine der seltenen> sinnvollen und sicheren Verwendungen einer union.
Gute Idee. Daran hatte ich gar nicht gedacht.
Karl H. schrieb:> be s. schrieb:>>> Wenn unterschiedliche Datentypen für die Arrays verwendet werden, legt>> man beide Arrays in einer union an, eigentlich auch schon nur, wenn man>> verschiedene Namen für die Arrays haben möchte. Eine der seltenen>> sinnvollen und sicheren Verwendungen einer union.>> Gute Idee. Daran hatte ich gar nicht gedacht.
Nach etwas Nachdenken zu folgendem Schluss gekommen: Mit dieser Variante
muss man sich einfach sicher sein, dass die Werte beim Verlassen der
Funktion, die das Array verwendet, verworfen werden dürfen, bzw. man
muss annehmen, dass die Werte verändert werden. Ansonsten könnte man
sich eine kleine Hilfsfunktion basteln:
Falls sich die Verwendung des zu nutzenden Speicherbereichs analog zu
einer lokalen Variablen organisieren lästt, dann geht alloca bzw. Arrays
variabler länge. Diese Arrays gibt's ab C99 (aber in C11 u.U. nicht
mehr).
Vorteil ist die einfache Verwendung und dass sie wesentlich weniger
Verwaltungsoverhead benötigen: Diese Arrays werden auf dem Stack
angelegt.
http://linux.die.net/man/3/alloca
1
#include<stdlib.h>
2
#include<stdint.h>
3
4
externvoiduse_array(size_t,uint8_t[]);
5
6
voidfunc(size_tn_bytes)
7
{
8
uint8_tarray[n_bytes];
9
use_array(n_bytes,array);
10
}
11
12
#include<alloca.h>
13
14
voidfunc_alloca(size_tn_bytes)
15
{
16
use_array(n_bytes,alloca(n_bytes));
17
}
Der erzeugte Code für beide Funktionen ist gleich; es wird i.W. Platz
auf dem Stack reserviert und am Ende wieder freigegeben.