Hi, ich möchte mir gern per malloc speicher auf dem heap holen. Und zwar habe ich ein struct: typedef struct datensatz{ int a,b,c; }Datensatz, *ptDatensatz; Somit kann ich mir beliebige Datensätze und Zeiger auf ebensolche anlegen. Nun habe ich ein weiteres struct: typedef struct name2{ pdDatensatz Zeiger; }XY,*ptXY; Nun möchte ich mir auf dem Heap speicher für ein xy struct besorgen also sagen ich: ptXY test; test=(ptXY)malloc(sizeof(XY)); Es wird danach ein solches element erstellt ABER wenn ich z.b. über: test->Zeiger->a=25; etwas hineinschreiben möchte, kommt ein Fehler, da ich anscheinend irgendwo ins nirvana schreiben möchte. Wenn ich im struct name2, statt pdDatensatz, Datensatz schreibe, funktionier es problemlos. D.h. den zeiger auf ein solches Struct durch ein "echtes" struct ersetze. Kann malloc einfach keinen speicher auf dem heap reservieren, weil er mit einem zeiger auf eine weitere datenstruktur nicht klarkommt? Momentan reicht es für mich im struct name2 einen "Datensatz Zeiger;" anzulegen... Trotzdem interessiert mich, warum es nicht funktioniert. Vielen Dank für Hilfe S.h
Ahh, wenn ich nochmal extra speicher vom typ datensatz auf dem heap organisier und dem test->Zeiger zuweise funktioniert es... Hmm, frage ich mich nur wo man sowas mal "nachlesen" kann. Try and error ist nicht so dolle ;)
Ein Pointer ist erstmal nix als eine Speicherstelle... die legst du dir an. Danach musst du das Objekt auf welches der Zeiger zeigen soll erst einmal zuweisen (oder wie hier erzeugen).
> typedef struct datensatz{ > int a,b,c; > }Datensatz, *ptDatensatz; Wwer hat eigentlich diese Unsitte gestartet, Zeiger immer hinter irgendwelchen kryptischen Kürzeln zu verstecken? > ptXY test; > test=(ptXY)malloc(sizeof(XY)); Den Cast sollte man hier weglassen. Er verschleiert eine potenzielle Fehlerquelle. > Es wird danach ein solches element erstellt ABER wenn ich z.b. über: > test->Zeiger->a=25; > etwas hineinschreiben möchte, kommt ein Fehler, da ich anscheinend > irgendwo ins nirvana schreiben möchte. Richtig. test->Zeiger ist ja noch uninitializiert. Der zeigt quasi in die Pampa. > Kann malloc einfach keinen speicher auf dem heap reservieren, weil er > mit einem zeiger auf eine weitere datenstruktur nicht klarkommt? Was heißt "nicht klarkommt"? Malloc gibt dir den Speicher für ein XY, und das enthält einen Zeiger auf Datensatz, also bekommst du genau das - einen Zeiger auf Datensatz. Du mußt jetzt dafür Sorge tragen, daß der auch auf einen Datensatz zeigt, bevor du ihn dereferenzierst. Malloc weiß auch gar nicht, was in deiner Struktur steht. Ja, es weiß ja noch nicht einmal, um welche Struktur es geht. Du übergibst ihm ja nur eine Anzahl von Bytes, und es gibt dir dann einen Speicherblock dieser Größe zurück. Es weiß nicht, daß in der Struktur ein Zeiger ist, und es weiß nicht, daß du gerne hättest, daß der gleich auf was Gültiges zeigt. Das will man auch gar nicht immer. > Trotzdem interessiert mich, warum es nicht funktioniert. Aus dem selben Grund, warum du 'test' nicht nutzen kannst, bevor du das malloc gemacht hast. Da erwartest du ja auch nicht, daß auf magische Weise dieser Zeiger schon auf was zeigt. Warum sollte das bei dem in XY anders sein?
das beste ist, du baust dir einen Construktor für die Struktur XY, den du nach dem speicher anfordern aufrufst und einen Destruktor, den du vor dem freigeben des Speichers aufrufst.
1 | typedef struct datensatz{ |
2 | int a; |
3 | int b; /* <- so siehts schöner aus und ist weniger fehleranfälliger*/ |
4 | int c; |
5 | }Datensatz; /* *pDatensatz <-- zeigerdefinitionen sind doof*/ |
6 | |
7 | void Datensatz_costruct(Datensatz* self) |
8 | {
|
9 | self->a = 0; |
10 | self->b = 0; |
11 | self->c = 0; |
12 | };
|
13 | |
14 | void Datensatz_destruct(Datensatz* self) |
15 | {
|
16 | };
|
17 | |
18 | |
19 | Nun habe ich ein weiteres struct: |
20 | typedef struct name2{ |
21 | Datensatz* Zeiger; |
22 | }XY,; /*zeigerdefinitionen sind doof*/ |
23 | |
24 | void XY_costruct(XY* self) |
25 | {
|
26 | self->Zeiger = malloc(sizeof(Datensatz)); |
27 | Datensatz_construct(self->Zeiger); |
28 | };
|
29 | |
30 | |
31 | void XY_destruct(XY* self) |
32 | {
|
33 | detensatz_destruct(self->Zeiger); |
34 | free(self->Zeiger); |
35 | };
|
36 | |
37 | void doSomething(void) |
38 | {
|
39 | |
40 | XY* myXY = malloc(sizeof(XY)); |
41 | XY_costruct(myXY); |
42 | |
43 | /* do something */
|
44 | |
45 | |
46 | XY_destruct(myXY); |
47 | free(myXY); |
48 | }
|
Wenn ein XY-Objekt sowiso immer ein Datensatzobjekt enthält, bräuchtest du natürlich keinen Pointer, sonder könntest es direkt als Member rein tun. Hängt natürlich von Anwendungsfall ab
Rolf Magnus schrieb: >> typedef struct datensatz{ >> int a,b,c; >> }Datensatz, *ptDatensatz; > > Wwer hat eigentlich diese Unsitte gestartet, Zeiger immer hinter > irgendwelchen kryptischen Kürzeln zu verstecken? Das scheint sowas wie ein Naturgesetzt der C Programmierer Evolution zu sein. Seltsamerweise haben die meisten die ich kenne (inklusive mir), diese Unsitte entwickelt und sie nach einiger Zeit wieder über Bord geworfen, als man merkte dass das keineswegs so clever ist, wie man am Anfang dachte. Man versteckt sich hier schon zuviel Information hinter einem typedef.
S.h. schrieb: > Hmm, frage ich mich nur wo man sowas mal "nachlesen" kann. > Try and error ist nicht so dolle ;) <...>
> frage ich mich nur wo man sowas mal "nachlesen" kann. Du hast 2 structs aber nur 1 malloc. Da sollte einem Blinden mit Krückstock auffallen, daß das nicht reicht. Das, worauf Zeiger verweist, gibt es (noch) nicht (es lohnt sich daher, gleich nach oder beim amlloc den neu geholten Speicherblock abzunullen (memset(,0,) oder calloc().) > Wer hat eigentlich diese Unsitte gestartet, Zeiger immer hinter > irgendwelchen kryptischen Kürzeln zu verstecken? Ein Ungar. http://de.wikipedia.org/wiki/Ungarische_Notation > Den Cast sollte man hier weglassen. Er verschleiert eine potenzielle > Fehlerquelle. Diesen cast darf man bei vielen Compilern in den meisten Compilerwarnungseinstellungen nicht weglassen, sie jammern sonst (obwohl der C-Standard sagt, dass void* auf alle * passt).
MaWin schrieb: >> Wer hat eigentlich diese Unsitte gestartet, Zeiger immer hinter >> irgendwelchen kryptischen Kürzeln zu verstecken? > > Ein Ungar. > http://de.wikipedia.org/wiki/Ungarische_Notation Das meine ich nicht. Ich meine die "Unsitte" sich einen typedef für den Pointer-Typ zu machen. Ich weiß, alle C-Programmierer machen anscheinend diese Phase durch (auch ich hatte das mal), aber irgendwann gibt jeder das wieder auf. Man versteckt sich einfach zu viel Information. Das Hungarian-p für Pointer ist das einzige Element der UN, das bei mir überlebt hat. > Diesen cast darf man bei vielen Compilern in den meisten > Compilerwarnungseinstellungen nicht weglassen, sie jammern sonst (obwohl > der C-Standard sagt, dass void* auf alle * passt). Dann sind das keine C-Compiler sondern C++ Compiler. Für einen C Compiler gibt es keinen Grund an dieser Stelle zu warnen. Die Operation ist ohne den Cast nicht gefährlicher als mit. Ganz im Gegenteil. Und Misverständnis gibt es an dieser Stelle auch keines (nicht so wie beim leidigen Thema "= in einem Ausdruck der als Bedingung benutzt wird"). Warum sollte ein C-Compiler daher hier eine Warnung aussprechen?
Karl heinz Buchegger schrieb: > MaWin schrieb: > >>> Wer hat eigentlich diese Unsitte gestartet, Zeiger immer hinter >>> irgendwelchen kryptischen Kürzeln zu verstecken? >> >> Ein Ungar. >> http://de.wikipedia.org/wiki/Ungarische_Notation > > Das meine ich nicht. > Ich meine die "Unsitte" sich einen typedef für den Pointer-Typ zu > machen. Ich weiß, alle C-Programmierer machen anscheinend diese Phase ich nicht! :-) > durch (auch ich hatte das mal), aber irgendwann gibt jeder das wieder > auf. Man versteckt sich einfach zu viel Information. > > Das Hungarian-p für Pointer ist das einzige Element der UN, das bei mir > überlebt hat. Ich nehme immer noch ein t für Typen und fp für Funktionszeiger. Das reicht aber dann auch m.E.. > ...
> Warum sollte ein C-Compiler daher hier eine Warnung aussprechen?
Weil praktisch alle C Compiler inzwischen C++ Compiler sind, die nur
noch einen Teil auschaltbar haben.
MaWin schrieb: >> Warum sollte ein C-Compiler daher hier eine Warnung aussprechen? > > Weil praktisch alle C Compiler inzwischen C++ Compiler sind, die nur > noch einen Teil auschaltbar haben. Ist kein Argument. Entweder er ist ein C Compiler oder er ist ein C++ Compiler. Oder er ist ein Kombicompiler, der sowohl C als auch C++ kann, aber niemals beides gleichzeitig. In diesem Sinne ist auch ein Kombicompiler entweder ein C Compiler oder ein C++ Compiler, wenn er sich eine bestimmte Compilation Unit vornimmt (und ja: oft ist einfach nur C++ eingestellt, selbst wenn man ein C-File compiliert. Das macht aber die Sache auch nicht richtiger) Es ist ein Irrtum zu denken, dass C++ 'nur' eine Erweiterung von C ist. Die Sprachen sind schon längst soweit auseinandergedriftet, dass die Ansicht, C als Subset von C++ zu betrachten, nicht mehr zutrifft. Mit demselben Grund könnte man sagen, dass ein C Compiler diesen Nicht-Cast anmeckern wird, weil ein Ada Compiler, der auf demselben Backend aufsitzt, das auch tut. Und das diese Argumentation etwas sinnfrei ist, braucht wohl nicht weiter diskutiert zu werden.
> Entweder er ist ein C Compiler oder er ist ein C++ Compiler.
Sag das dem jeweiligen Hersteller, nicht mir.
Ich passe mich der Realität an,
du darfst gerne weiter in Theorie schwimmen.
Karl Heinz mag vieles tun, aber "in der Theorie schwimmen" tut er nicht. Die Realität in Form des im Visual Studio 2008 enthaltenen Compilers gibt keine Warnung aus, wenn das Konstrukt int* bla; bla = malloc(10); als C-Code übersetzt wird.
MaWin schrieb: >> Entweder er ist ein C Compiler oder er ist ein C++ Compiler. > > Sag das dem jeweiligen Hersteller, nicht mir. > Ich passe mich der Realität an, OK. Realität: Welcher C Compiler gibt bei besagtem Konstrukt eine Warnung aus und wie lautet eigentlich der Text der Warnung? Das würde mich nämlich ehrlich gesagt interessieren: Wovor warnt denn der Compiler bei einer Zuweisung aus dem malloc an einen Pointer ohne Cast? Was ist der Grund der Warnung? "Assignment from void Pointer without a cast" wirds ja wohl nicht sein. Denn das ist ziemlich nichtssagend, zumal es vom C-Standard ausdrücklich erlaubt ist und alle C Compiler diese Zuweisung seit Anbegin der C-Welt sauber und ohne Murren machen. Ausserdem würde eine derartige Warnung nichts bringen, weil jeder Programmierer ganz einfach nachsehen würde, welchen Typ der Pointer hat und dorthin castet. Das eigentlich Problem eines malloc, nämlich dass die Speicheranforderung groß genug sein muss, ist davon aber unberührt. Damit hätte man eine Warnung, die vor ... nichts Sinnvollem ... warnt. Das void Pointer ein Problem für sich darstellen ist eine andere Sache. Nur geht es in C nun mal nicht anders, so wie die Sprache definiert ist. Ich wäre auch glücklich, wenn dem nicht so wäre. Ich bleibe trotzdem dabei: Solange es sich um C-Code handelt (wie gesagt, bei der Übernahme nach C++ sieht die Welt anders aus), ist dieser Cast einer von der Sorte, die man nicht machen sollte. Es wäre schon viel gewonnen, wenn C-Programmierer einen Cast nicht als Allheilmittel für alles Mögliche ansehen, sondern als das was sie sind: Als Waffen, die nützlich sein können und auch notwendig sind, die man aber so sparsam wie möglich einsetzen muss.
> Welcher C Compiler gibt bei besagtem Konstrukt eine Warnung aus
Hmm, ihr könntet richtig liegen,
der, den ich im Verdacht hatte (VS2005), der hatte in dem Projekt, in
dem casts vor malloc notwendig waren, ein eigenes mit char * definiertes
malloc, und nicht das aus stdlib, wie ich gerade ermittelt habe.
Trotzdem kommt mir das Verhalten nicht unbekannt vor, das gab es schon
früher.
Warum sollte der Compiler eine Warnung ausgeben? int* bla; bla = malloc(10); sagt nur, dass du dir ein Array von ints am Heap holst. Also warum soll er schreien?
oh, ich vergaß: void *malloc(size_t size); und das darf alles sein.
zwieblum schrieb:
> Warum sollte der Compiler eine Warnung ausgeben?
Compiler warnen schon mal (zu Recht) seltsame und gefährliche Konstrukte
an.
An und für sich ist das ja gar nicht so dumm
Bei
pointer_auf_irgendwas = void_pointer
sollte man sich sehr sicher sein, dass void_pointer einen Wert von einem
anderen pointer_auf_irgendwas erhalten hat. Ansonsten ist Ärger
vorprogrammiert.
Nur: Wie willst du als Programmierer das sicherstellen?
Wenn ich einen expliziten Datentyp hätte, dann hätte ich zuallererst
keinen void-Pointer genommen. Der ist mein allerletzter Ausweg. Da hilft
es auch nichts, wenn ich den Compiler mit einem Cast besänftige.
Entweder der void-Pointer passt oder er passt nicht. Da hilft es mir
auch nichts, wenn mich der Compiler mit einer Warnung darauf hinweist
"Überprüf das bitte nochmal und mach einen Cast rein um mir zu zeigen,
dass du dir das überlegt hast"
Das ist das generelle Problem einer Zuweisung eines void-Pointers.
Bei malloc kommt noch hinzu:
Wohin soll ich denn casten?
Ich hab ja ohnehin keine andere Wahl als auf den Datentyp des Pointers.
Wenn daher mit Cast die Datentypen nicht übereinstimmen, dann ist das
einfach nur ein simpler Tippfehler, den ich nicht hätte, wenn ich den
Cast gar nicht gemacht hätte. Denn: Der Datentyp im Cast steht in
keinerlei Zusammenhang mit der Allokierungsgröße, die ich dem malloc
mitgebe. Und dort liegt doch das eigentliche Problem an dieser Stelle!
Und das Argument, dass ich zb in diesem Fall
int * pI;
pI = (int*) malloc( 10 * sizeof( int ) );
vom Compiler eine (gewünschte) Warnung bekomme, wenn sich der Datentyp
von pI ändert und ich auf den malloc vergesse
double * pI;
pI = (int*) malloc( 10 * sizeof( int ) );
(was ziemlich offensichtlich ein Versehen ist und vom Compiler mit einem
Error quittiert wird) kann ich ganz leicht entkräften, indem ich sage,
schreib das doch von vorneherein gleich so:
int * pI;
pI = malloc( 10 * sizeof(*pI) );
Jetzt kann ich aus dem int* einen double* machen OHNE mich um den malloc
kümmern zu müssen. Es wird nachwievor Platz für ein Array von 10
Elementen angelegt, nur sind es in der 2.ten Version eben 10 double und
nicht mehr 10 int, was ja dann wohl auch die Absicht war als ich den
int* gegen einen double* ausgetauscht habe. Was Besseres kann ich mir
doch gar nicht wünschen, als das ich das so formulieren kann, dass mir
der Compiler Arbeit abnimmt und Konsistenz sicherstellt. Und wenn pI ein
Pointer auf struct Hotzenplotz ist, dann wird eben ohne Änderung im
malloc Platz für 10 Hotzenplotz Objekte reserviert.
Ich habe mit dieser Schreibweise gar keine Chance einen Fehler zu
machen! Den einzigen Fehler den ich machen kann ist der, das ich keinen
Prototypen für malloc im Scope habe und mein Warning-Level zu klein
eingestellt ist. Und ausgerechnet diesen potentiellen Fehler unterdrücke
ich dann mit einem Cast? Das kanns doch nicht sein.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.