Forum: PC-Programmierung Malloc speicher auf dem Heap holen.


von S.h. (Gast)


Lesenswert?

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

von S.h. (Gast)


Lesenswert?

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 ;)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

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).

von Rolf Magnus (Gast)


Lesenswert?

> 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?

von Vlad T. (vlad_tepesch)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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.

von P. S. (Gast)


Lesenswert?

S.h. schrieb:

> Hmm, frage ich mich nur wo man sowas mal "nachlesen" kann.
> Try and error ist nicht so dolle ;)

<...>

von MaWin (Gast)


Lesenswert?

> 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).

von Karl H. (kbuchegg)


Lesenswert?

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?

von Klaus W. (mfgkw)


Lesenswert?

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..

> ...

von MaWin (Gast)


Lesenswert?

> 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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

> 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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von MaWin (Gast)


Lesenswert?

> 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.

von zwieblum (Gast)


Lesenswert?

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?

von zwieblum (Gast)


Lesenswert?

oh, ich vergaß:

void *malloc(size_t size);

und das darf alles sein.

von Karl H. (kbuchegg)


Lesenswert?

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
Noch kein Account? Hier anmelden.