Speicher allozieren
-
Hallöchen!
Ich habe da ein paar Verständnisprobleme mit dem Freigeben von Speicher.
Ich habe ein struct IntArray:struct IntArray { int length; int *field; };
Nun will ich speicher für das struct und für das array freigeben:
struct IntArray *a = (struct IntArray *) malloc(??); a->field = (int *)malloc(??);
Was ich jetzt nicht verstehe ist, dass es scheinbar egal ist, was ich für eine Zahl bei "??" eintrage. (Ich kann sogar 0 eintragen)
Mein Programm funktioniert nur nicht, wenn ich den malloc beim a->field nicht mache.
Wenn ich den malloc bei *a nicht mache, dann ist mein Array unabhängig von der größe des eingelesenen arrays nur 1 element groß.
Eigentlich würde ich es so machen:struct IntArray *a = (struct IntArray *) malloc(sizeof(struct IntArray)+length*sizeof(int)); a->field = (int *)malloc(length*sizeof(length));
Das Verständnis dafür brauche ich vor allem um im Anschluss zu wissen, wie ich den reservierten Speicher wieder freigebe.
Zuerst dachte ich an soetwas:free(a->field); free(a);
Jedoch kann ich dann immernoch auf das zuvor eingelesene array zugreifen.
Hiermit klappt es zwar, aber ich weiß nicht ob das wirklich richtig ist:
free(a);
-
Dein Problem ist erstmal so genanntes undefiniertes Verhalten. Das heißt: Es kann alles passieren. Es kann auch einfach funktionieren. Oder abstürzen. Oder Pizza bestellen. Die Sprache definiert nicht, was da passieren soll.
Richtig ist es so:struct IntArray { int length; int *field; }; int main(void) { struct IntArray *a = malloc(sizeof *a); // sizeof(IntArray) a->length = 5; a->field = malloc(a->length * sizeof *a->field); // 5 * sizeof(int) // .. free(a->field); free(a); // Hiernach darfst du weder a->field, noch a dereferenzieren. (Auf den Wert der hinter den Zeigern steht zugreifen.) // Das kann funktionieren, muss aber nicht. }
Allerdings stellt sich mir die Frage nach dem Sinn, auch das IntArray selbst im dynamischen Speicher zu erstellen. Könntest du das nicht einfach auf dem Stack anlegen?
-
Achso, also kann es, wenn ich malloc(0) mache funktionieren und ein anderes mal wieder nicht, habe ich das richtig verstanden?
Das ich das struct auch dynamisch allozieren soll hab ich bei jedem Beispiel im Internet bisher so gesehen. Mein struct IntArray ist auch in wirklichkeit in einem modul intarray.c mit intarray.h definiert.Leider verstehe ich nicht was du hiermit nun meinst:
cooky451 schrieb:
Könntest du das nicht einfach auf dem Stack anlegen?
Außerdem hab ich gelesen, dass malloc normalerweise void zurückgibt und man deswegen den gewünschten Type casten soll, das machst du ja auch nicht, wieso?
Also wenn ich für das struct weniger Speicher alloziere als für das array im struct, dann ergibt das für mich irgendwie keinen Sinn. (oder ich habe einen Denkfehler)
-
Iberion schrieb:
Achso, also kann es, wenn ich malloc(0) mache funktionieren und ein anderes mal wieder nicht, habe ich das richtig verstanden?
Jop.
Iberion schrieb:
Das ich das struct auch dynamisch allozieren soll hab ich bei jedem Beispiel im Internet bisher so gesehen.
Link?
Iberion schrieb:
Leider verstehe ich nicht was du hiermit nun meinst:
Das hier:
struct IntArray { int length; int *field; }; int main(void) { struct IntArray a; a.length = 5; a.field = malloc(a.length * sizeof *a.field); // 5 * sizeof(int) // .. free(a.field); }
Wirklich sinnvoll wird das natürlich erst, wenn du dir noch Funktionen zum erstellen/ändern der Größe etc. erstellst. Fragt sich nur, warum du dann nicht gleich C++ nutzt.
Iberion schrieb:
Außerdem hab ich gelesen, dass malloc normalerweise void zurückgibt und man deswegen den gewünschten Type casten soll, das machst du ja auch nicht, wieso?
In C ist sowohl die Konvertierung T* -> void* als auch void* -> T* implizit möglich. (T* sei ein beliebiger Objektzeiger, also kein Funktionszeiger.)
In C++ ist nur T* -> void* implizit möglich, also müsstest du casten. Aber einige C Jünger sehen das gar nicht gerne.Iberion schrieb:
Also wenn ich für das struct weniger Speicher alloziere als für das array im struct, dann ergibt das für mich irgendwie keinen Sinn. (oder ich habe einen Denkfehler)
Ja, da hast du einen Denkfehler. Das struct hält ja kein Array. Im struct sind nur ein int, und ein Pointer auf ein int. Kein Feld. Dieses erzeugst/reservierst du erst beim zweiten malloc, und der Zeiger auf int zeigt dann auf den Anfang dieses Feldes. Aber der Speicherbedarf für den Zeiger wird deshalb ja nicht größer. Pointer solltest du dir noch mal angucken, wenn du das nicht offensichtlich findest.
-
Ich habe eine Funktion zum erstellen eines IntArray:
struct IntArray * createintarray(int length) { struct IntArray *a; a->length = length; a->field = malloc(a->length*sizeof(int)); return a; }
Leider kann ich es hier nicht so machen, weil ich ja einen Pointer auf ein IntArray zurückgeben muss.
struct IntArray * createintarray(int length) { struct IntArray a; ... }
Am liebsten würde ich es ja auch so machen:
struct IntArray * createintarray(int length) { struct IntArray a; a.length = length; a.field = malloc(a.length*sizeof(int)); return ?; }
Jedoch weiß ich dann nicht, was ich zurückgeben muss um einen Pointer auf a zu haben.
Ist halt eine Aufgabe an der Uni, da soll ich C benutzen.
-
Iberion schrieb:
Leider verstehe ich nicht was du hiermit nun meinst:
cooky451 schrieb:
Könntest du das nicht einfach auf dem Stack anlegen?
Außerdem hab ich gelesen, dass malloc normalerweise void zurückgibt und man deswegen den gewünschten Type casten soll, das machst du ja auch nicht, wieso?
Lokale Variablen (die in einer Funktion definiert werden) werden auf dem Stack (-> http://de.wikipedia.org/wiki/Stapelspeicher) angelegt.
Für die musst du den Specher nicht extra anfordern.In deinem Fall fällt das malloc für a weg.
-
@Iberion
Mit der Funktionssignatur musst du da in der Tat dynamisch Speicher reservieren. Macht aber keinen Sinn. Wenn du darfst, ändere einfach die Signatur zu:struct IntArray createintarray(int length) { struct IntArray a; a.length = length; a.field = malloc(a.length*sizeof(int)); return a; }
Wenn du das nicht darfst, frag mal deinen Prof was er sich dabei gedacht hat. Zudem sollte man eigentlich auch überprüfen ob malloc nicht 0 zurückgegeben hat, aber na ja bei so kleinen Uni Programmen kann man's auch lassen.
-
Dankeschön! Ich hab mich da halt sehr an die Folien des Übungsscript gehalten, nun verstehe ich zumindest einiges besser.
-
Sorry, wenn ich blöd frage, aber:
struct IntArray * createintarray (int length) { struct IntArray a; a.length = length; a.field = malloc(a.length*sizeof(int)); return &a; } int main (void) { struct IntArray *b; // b ist jetzt ein Pointer auf ein nicht initialisiertes IntArray ohne allokierten Speicherbereich b = createintarray (5); // Ich hätte gedacht, mit b kann ich hier jetzt nicht arbeiten // schließlich zeigt b auf einen Speicherbereich, der im Scope einer anderen Funktion liegt? // Würde man b als Argument an createintarray übergeben und die Funktion anpassen, sähe es natürlich anders aus ... }
-
Ja.
-
@Iberion: Deine Ausgangslösung war teilweise richtig.
@StupidQuestion: Wird irgendwann mal crashen.
Schon mal die Lösung mit GCC kompiliert? MS VStudio würde höchstwahrscheinlich auch meckern.
GCC warnung: "warning: function returns address of local variable"Bin der Meinung, dass die richtige Lösung so aussieht:
#include <stdlib.h> struct IntArray { int length; int *field; }; // 1.te Version struct IntArray* createIntArray (int length) { if(length < 0) { //auch <= sinnvoll return 0; //arrays mit negativen Groessen unzulaessig } struct IntArray* a = (struct IntArray*) malloc(sizeof(struct IntArray)); if(a != 0) { int* mem = (int*) malloc(length); if(mem == 0) { //Allokation fehlgeschlagen // bereits allozierten Speicher freigeben. free((void*)a); return 0; } //Groesse & Pointer zuweisen a->length = length; a->field = mem; return a; } //sonst Allokation fehlgeschlagen return 0; } // 2.te Version struct IntArray* createIntArray2 (int length) { if(length < 0) { //auch <= sinnvoll return 0; //arrays mit negativen Groessen unzulaessig } int* mem = (int*) malloc(length); if(mem == 0) { //Allokation fehlgeschlagen return 0; } struct IntArray* a = (struct IntArray*) malloc(sizeof(struct IntArray)); if(a != 0) { //Groesse & Pointer zuweisen a->length = length; a->field = mem; return a; } //Allokation fehlgeschlagen free( (void*) mem);// bereits allozierten Speicher freigeben. return 0; } int main (void) { struct IntArray *b; // b ist jetzt ein Pointer auf IRGENDWAS im Speicher // b kann, muss aber nicht, 0 sein b = createIntArray (5); if(b != 0) { // mit b weiterarbeiten ... } return 0; }
Spaßeshalber 2 Versionen. Kommentare erzählen alles.
Inkl. Null-Pointer-Überprüfungen.
Casts nach void* eigentlich nicht notwendig.
-
dimiKL schrieb:
@StupidQuestion: Wird irgendwann mal crashen.
Hast du seine Kommentare mal gelesen?
dimiKL schrieb:
Bin der Meinung, dass die richtige Lösung so aussieht:
Bitte cpp Tags nutzen.
Statt int lieber size_t, dann bleibt dir auch der Test erspart. (Aber gut, war ja so vorgegeben.)
Bei dir ist einmal der "Good-Path" und einmal der "Bad-Path" im if. Lieber einheitlich bleiben.
Rückgabetyp von malloc musste in C nicht casten.
Du alloziierst nur length statt length * sizeof(int) Byte!dimiKL schrieb:
Casts nach void* eigentlich nicht notwendig.
Warum machst du es dann?
-
Apropo Speicher alloziieren:
Wie macht ihr das bei zweidimensionalen Arrays? Macht ihr für jede Zeile ein malloc? Ich habe mir angewöhnt gleich den kompletten Speicher (Zeile * Spalte) zu alloziieren und dann mit Pointer-Arithmetik zu arbeiten. Das spart die vielen Aufrufe und Modi-Wechsel.
-
Steffo schrieb:
kompletten Speicher (Zeile * Spalte)
Ist ja auch richtig, wie du im C++ Forum gefühlt tausendfach nachlesen kannst.
-
Steffo schrieb:
Apropo Speicher alloziieren:
Wie macht ihr das bei zweidimensionalen Arrays? Macht ihr für jede Zeile ein malloc?Das wäre kein zweidimensionales Array mehr. Man muss schon wissen, was man möchte.
-
Was bringt dir ein zweidimensionales Array außer Komfort?
-
Steffo schrieb:
Was bringt dir ein zweidimensionales Array außer Komfort?
Steffo schrieb:
den kompletten Speicher (Zeile * Spalte)
-
@ccoky451: Hast recht lengthsizeif(int) müsste es heißen.
Casts? Einfach mal zur Vermeidung von Warnungen, bin sicher, dass es Compiler gibt, die nicht automatisch " int " auch als " void* " akzeptieren.
-
dimiKL schrieb:
Casts? Einfach mal zur Vermeidung von Warnungen, bin sicher, dass es Compiler gibt, die nicht automatisch " int* " auch als " void* " akzeptieren.
Ja, sogenannte C++-Compiler. Habe aber keine Ahnung, was dieses "C++" sein soll. Bestimmt ein Javadialekt.
Du verhinderst also bloß, dass du Fehler bemerkst.
-
SeppJ schrieb:
Habe aber keine Ahnung, was dieses "C++" sein soll. Bestimmt ein Javadialekt.
Das C steht in diesem Fall für Coffee.