als Anfänger beschäftige ich mich zur Zeit genauer mit
Zeiger um mein Verständnis zu vertiefen.
Meine Experimente sind aber offensichtlich angetan
um mich noch mehr zu verwirren:
Der folgende Code:
<--snipp-->
unsigned int Feld[5]={ 1,3,5,7,11 };
printf("Feld[1]:%d\n",*(Feld+1));
printf("Feld[1]:%d\n",*(Feld+1)+1);
<--snipp-->
liefert die Ausgabe:
Feld[1]:3
Feld[2]:4
das Ganze jedoch so:
<--snipp-->
unsigned int **ZFeld=NULL;
ZFeld=(unsigned int **)calloc(5,sizeof(unsigned int));
(unsigned int)*(ZFeld+1)=3;
printf("Feld[1]:%d\n",*(ZFeld+1));
printf("Feld[1]:%d\n",*(ZFeld+1)+1);
<--snipp-->
liefert die Ausgabe:
Feld[1]:3
Feld[1]:7
Was mache ich falsch?
Danke für jede Kritik und Hinweis!
mfg
john
> als Anfänger beschäftige ich mich zur Zeit genauer mit
> Zeiger um mein Verständnis zu vertiefen.
An diesem Kapitel ist schon so mancher Anfänger verzweifelt :-)
> unsigned int **ZFeld=NULL;
¯¯
Warum **? Zeiger auf Zeiger? Ist es das, was Du wolltest?
> ZFeld=(unsigned int **)calloc(5,sizeof(unsigned int));
Warum initialisierst Du ZFeld, wenn Du ihm im nächsten Schritt sowieso
einen Wert zuweist? Das ist zwar nicht falsch, aber überflüssig.
Der Cast hingegen ist nicht nur überflüssig, sondern auch gefährlich,
weil er Fehler kaschieren kann, z.B. ein fehlendes #include <stdlib.h>.
ZFeld zeigt nicht auf unsigned int, sondern auf unsigned int*. Die Größe
eines Feldelementes ist also nicht sizeof(unsigned int), sondern
sizeof(unsigned int*). Auf der sicheren Seite bist Du hier, wenn Du
sizeof *ZFeld schreibst. Dann brauchst Du diese Stelle nie mehr zu
ändern, auch dann nicht, wenn sich der Typ von ZFeld irgendwann mal
ändern sollte.
calloc initialisiert einen Speicherbereich mit binären Nullen. Der
Standard garantiert aber nicht, dass Nullpointer so dargestellt werden.
> (unsigned int)*(ZFeld+1)=3;
Hier wird's richtig schlimm: Zfeld ist unsigned int**, *Zfeld folglich
unsigned int*. Du weist den Compiler aber explizit an, an diese Adresse
einen int zu speichern. Das erzeugt undefiniertes Verhalten.
> printf("Feld[1]:%d\n",*(ZFeld+1));
> printf("Feld[1]:%d\n",*(ZFeld+1)+1);
Auch hier undefiniertes Verhalten: Du benutzt ein Format, welches für
int ist, um einen unsigned int* auszugeben.
Gruß. Claus
Denkfehler, nun bin ich dort wohin ich wollte:
unsigned int *ZFeld=NULL;
Warum ich ** genommen habe? Keine Ahnung!
Zeigt mir das Thema Zeiger ist noch nicht ganz richtig
verdaut!
> Warum initialisierst Du ZFeld, wenn Du ihm im nächsten Schritt sowieso
> einen Wert zuweist? Das ist zwar nicht falsch, aber überflüssig.
>
> Der Cast hingegen ist nicht nur überflüssig, sondern auch gefährlich,
> weil er Fehler kaschieren kann, z.B. ein fehlendes #include <stdlib.h>.
>
> ZFeld zeigt nicht auf unsigned int, sondern auf unsigned int*. Die Größe
> eines Feldelementes ist also nicht sizeof(unsigned int), sondern
> sizeof(unsigned int*). Auf der sicheren Seite bist Du hier, wenn Du
> sizeof *ZFeld schreibst. Dann brauchst Du diese Stelle nie mehr zu
> ändern, auch dann nicht, wenn sich der Typ von ZFeld irgendwann mal
> ändern sollte.
Danke - die Zeilen waren sehr lehrreich!
ZFeld=calloc(5,sizeof ZFeld);
*(ZFeld+1)=3;
printf("Feld[1]:%d\n",*(ZFeld+1));
printf("Feld[1]:%d\n",(*(ZFeld+1))+2);
nun funktioniert (ob es den strengen Blick eines
Profis stand hält ????)
Dein Posting werd ich ausdrucken und aufheben -
hat einiges an Verständnis gebracht !!
Besten, besten Dank !!
john
John Pinto hat geschrieben:
> ZFeld=calloc(5,sizeof ZFeld);
^^^^^
Das meinst du doch nicht so, wie du es hier schreibst? Du
wolltest wohl int sagen.
> *(ZFeld+1)=3;
>
> printf("Feld[1]:%d\n",*(ZFeld+1));
> printf("Feld[1]:%d\n",(*(ZFeld+1))+2);
Klammern: a b ba
Hi, hi. Wenn man nicht weis, in welcher Reihenfolge Operatoren
abgearbeitet werrden, dann schmeißt man mit Klammern um
sich:-). Sieht nicht immer profimäßig aus, aber ich persönlich
mache es auch oft so. Ist zumindest weder falsch noch
schädlich. Sprich wenn man eine Klammer auch hätte weglassen
können um das Selbe zu erreichen, dürfte sich der
resultierende Code auch nicht unterscheiden.
Aber du mußt schon zugestehen, wenn du die Klammern b-b für
nötig häst, dann sind logischerweise die Klammern a-a
überflüssig - oder umgekehrt. Aber schon richtig, sicher ist
sicher. Übrigens sind die Klammern a-a überflüssig.
CU Rollo
--
Hier entsteht in Kürze eine neue Sig-Präsenz.
> Moin,
>
> John Pinto hat geschrieben:
>
>> ZFeld=calloc(5,sizeof ZFeld);
> ^^^^^
> Das meinst du doch nicht so, wie du es hier schreibst? Du
> wolltest wohl int sagen.
Nein, er haette "ZFeld = malloc(5 * sizeof *ZFeld)" sagen wollen
sollen. Es ist zwar garantiert, dass sizeof(unsigned int) gleich
sizeof(int) ist, aber warum sollte jemand den Typ hinschreiben
wollen, wenn er einen passenden Ausdruck vom richtigen Typ
griffbereit hat?
Kurt Watzka
> On Tue, 09 Dec 2003 11:10:10 +0100, Claus Reibenstein wrote:
>
>>Warum **? Zeiger auf Zeiger? Ist es das, was Du wolltest?
>
> Denkfehler, nun bin ich dort wohin ich wollte:
>
> unsigned int *ZFeld=NULL;
Wusst' ich's doch :-)
>>[...] Auf der sicheren Seite bist Du hier, wenn Du
>>sizeof *ZFeld schreibst. [...]
Und was schreibst Du?
> ZFeld=calloc(5,sizeof ZFeld);
Siehst Du den Unterschied?
> printf("Feld[1]:%d\n",*(ZFeld+1));
> printf("Feld[1]:%d\n",(*(ZFeld+1))+2);
>
> nun funktioniert (ob es den strengen Blick eines
> Profis stand hält ????)
Nein, hält es nicht: "%d" ist für int, *(ZFeld+irgendwas) ist aber
unsigned int. Dafür musst Du "%u" benutzen.
> Dein Posting werd ich ausdrucken und aufheben -
> hat einiges an Verständnis gebracht !!
Das freut mich :-)
Gruß. Claus
>
> Und was schreibst Du?
>
>> ZFeld=calloc(5,sizeof ZFeld);
>
> Siehst Du den Unterschied?
ZFeld=calloc(5,sizeof *ZFeld);
Habs richtig gestellt.
Gibt es eigentlich einen Grund malloc statt calloc zu verwenden?
So sind doch zumindest Nullen drinnen, anderfalls undefinierte
Werte solange nicht initialisiert wird?
>
>> printf("Feld[1]:%d\n",*(ZFeld+1));
>> printf("Feld[1]:%d\n",(*(ZFeld+1))+2);
>>
>> nun funktioniert (ob es den strengen Blick eines Profis stand hält
>> ????)
>
> Nein, hält es nicht: "%d" ist für int, *(ZFeld+irgendwas) ist aber
> unsigned int. Dafür musst Du "%u" benutzen.
printf("Feld[1]:%u\n",*(ZFeld+1));
printf("Feld[1]:%u\n",(*(ZFeld+1))+2);
Danke!
Es wundert mich das der Kompiler nicht schreit, selbst wenn ich das
Programm mit -pedantic und -Wall übersetze.(Ich verwende gcc).
Macht der in diesem Fall keinen Typencheck?
Herzlichen Dank nochmals allen für die Hilfe!
mfg
john
>On Tue, 09 Dec 2003 18:18:46 +0100, Claus Reibenstein wrote:
>
>>
>> Und was schreibst Du?
>>
>>> ZFeld=calloc(5,sizeof ZFeld);
>>
>> Siehst Du den Unterschied?
>
>ZFeld=calloc(5,sizeof *ZFeld);
>
>Habs richtig gestellt.
>
>Gibt es eigentlich einen Grund malloc statt calloc zu verwenden?
Ja. Man möchte nicht für eine Ware zahlen, die man nicht bestellt hat.
>So sind doch zumindest Nullen drinnen, anderfalls undefinierte
>Werte solange nicht initialisiert wird?
Ja. Häufig stört das nicht.
--
Horst
>Gibt es eigentlich einen Grund malloc statt calloc zu verwenden?
>So sind doch zumindest Nullen drinnen, anderfalls undefinierte
>Werte solange nicht initialisiert wird?
Ein mit calloc() geholter Speicherbereich fuer Zeiger ist ganz genauso
uninitialisiert, und der/die Zeiger ganz genauso unbenutzbar, als
wuerde man nicht wissen, dass er mit binaeren Nullen gefuellt ist.
b.
Mit calloc() geholter Speicher _ist_ mit 0 initialisiert, mit malloc()
geholter nicht. Schreibt der Standard so vor...
> wuerde man nicht wissen, dass er mit binaeren Nullen gefuellt ist.
...oder wolltest du das damit sagen?
--
Jochen Erwied | home: joc...@erwied.de +49-208-38800-18, FAX: -19
Sauerbruchstr. 17 | work: j...@mbs-software.de +49-2151-7294-24, FAX: -50
D-45470 Muelheim | First sightings... <16...@laura.UUCP> 1989/10/11 18:06
> Andreas Burmester <a.bur...@hamburg.de> wrote:
>
>> Ein mit calloc() geholter Speicherbereich fuer Zeiger ist ganz genauso
>> uninitialisiert, und der/die Zeiger ganz genauso unbenutzbar, als
>
> Mit calloc() geholter Speicher _ist_ mit 0 initialisiert, mit malloc()
> geholter nicht. Schreibt der Standard so vor...
Was der Standard aber nicht vorscheibt, ist, dass "alle Bytes auf 0 gesetzt"
ein Nullzeiger ist. Und der Speicher wird eben nicht "mit 0 initialisiert"
sondern es werden alle Bytes auf 0 gesetzt. Das kann, muss aber keineswegs,
eine passende Initialisierung fuer Zeiger und Fliesskommazahlen sein.
Kurt Watzka
>> Mit calloc() geholter Speicher _ist_ mit 0 initialisiert, mit malloc()
>> geholter nicht. Schreibt der Standard so vor...
>
> Was der Standard aber nicht vorscheibt, ist, dass "alle Bytes auf 0 gesetzt"
> ein Nullzeiger ist. Und der Speicher wird eben nicht "mit 0 initialisiert"
> sondern es werden alle Bytes auf 0 gesetzt. Das kann, muss aber keineswegs,
> eine passende Initialisierung fuer Zeiger und Fliesskommazahlen sein.
Klar.
Ob es jetzt Sinn macht, dass der komplette Speicher auf 0 gesetzt ist,
steht natürlich auf einem anderen Blatt. Wenn man den gerade geholten
Speicher sowieso noch mal anpacken muss, um eine sinnvolle Initialisierung
zu haben, dann ist malloc() die geschicktere Wahl, die Initialisierung in
calloc() an der Stelle sinnfrei.
Bei ordinalen Typen (hiessen die so?) wie char oder int macht calloc()
hingegen schon Sinn.
> Ob es jetzt Sinn macht, dass der komplette Speicher auf 0 gesetzt ist,
> steht natürlich auf einem anderen Blatt. Wenn man den gerade geholten
> Speicher sowieso noch mal anpacken muss, um eine sinnvolle Initialisierung
> zu haben, dann ist malloc() die geschicktere Wahl, die Initialisierung in
> calloc() an der Stelle sinnfrei.
>
> Bei ordinalen Typen (hiessen die so?) wie char oder int macht calloc()
> hingegen schon Sinn.
Ich denke dabei manchmal an "Die Sinnmacher".
In Sendungen mit geschliffenen Dialogen in korrektem Deutsch
(Raumschiff Enterprise, Deep Space Nine, CSI)
hört man "das macht Sinn" tatsächlich niemals.
--
Mit freundlichen Grüßen
Helmut Schellong v...@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
>Jochen Erwied wrote:
>
>> Ob es jetzt Sinn macht, dass der komplette Speicher auf 0 gesetzt ist,
>> steht natürlich auf einem anderen Blatt.
Wenn man weiß, daß die eigene Plattform NULL-Zeiger = Alle Bits 0 hat, dann
schon. Dadurch hat man nämlich ein zusätzliches Debug-Feature, vor allem auf
CPUs mit MMU. In Productive Environments spielt das dann aber keine Rolle
mehr.
>In Sendungen mit geschliffenen Dialogen in korrektem Deutsch
>(Raumschiff Enterprise, Deep Space Nine, CSI)
>hört man "das macht Sinn" tatsächlich niemals.
Dafür aber andere Anglizismen.
Gruß, Bodo
--
MS Outlook Express?->[DE: http://piology.org/ILOVEYOU-Signature-FAQ.html]
@@@@@ GEGEN TCG aka. TCPA: @@@@@ [DE: http://www.againsttcpa.com]
>Mit calloc() geholter Speicher _ist_ mit 0 initialisiert, mit malloc()
>geholter nicht. Schreibt der Standard so vor...
Bitte lies sorgfaeltiger; ich habe nicht geschrieben, dass er "nicht
initialisiert" sei, sondern - Komparativ! - "genauso uninitialisiert"
ist, als:
>> wuerde man nicht wissen, dass er mit binaeren Nullen gefuellt ist.
>...oder wolltest du das damit sagen?
Gut, in anderen Worten: Fuer den Umgang mit einen Zeiger macht es
keinen Unterschied, ob man gar nicht weiss, was er enthaelt, oder ob
man irgendwelche binaeren Werte 'rein gezaubert hat. In beiden Faellen
hat er keinen definierten (Zeiger-)Wert, ist unbenutzbar und insofern
uninitialisiert.
Insbesondere bedeuten - wie im calloc()-Fall - binaere Nullen nicht,
dass man so einen oder mehrere Nullpointer auf einen Rutsch generieren
kann.
Klarer?
b.
Auch dann ist das meiner Erfahrung nach schaedlich, weil es Tools wie valgrind
aushebelt, die Zugriffe auf nicht initialisierte Daten feststellen koennen,
waehrend ein Indirektion eines NULL Zeigers nicht immer fehlschlagen muss
(auch nicht auf CPUs mit MMU). Unter AIX fuehrt lesen von Adresse 0 zum
Beispiel nicht zu Fehlern, weil der icc so schnelleren Code erzeugen kann.
Ein weiterer Nachteil ist, dass Du durch das "Anfassen" des Speichers dafuer
sorgst, dass das Betriebssystem diesen auch tatsaechlich bereitstellen muss,
was heftige Plattenaktivitaet (und langsame Programme) nach sich ziehen kann.
Mit anderen Worten: Auch hier lohnt sich unportables Programmieren in den
seltensten Faellen:-)
Gruss
Uz
--
Ullrich von Bassewitz u...@spamtrap.musoftware.de
17:51:28 up 9 days, 23:14, 10 users, load average: 0.00, 0.03, 0.09
>Bodo Thiesen <bot...@gmx.de> wrote:
>> Wenn man weiß, daß die eigene Plattform NULL-Zeiger = Alle Bits 0 hat, dann
>> schon. Dadurch hat man nämlich ein zusätzliches Debug-Feature, vor allem auf
>> CPUs mit MMU. In Productive Environments spielt das dann aber keine Rolle
>> mehr.
>
> Auch dann ist das meiner Erfahrung nach schaedlich, weil es Tools wie valgrind
> aushebelt,
Klar, dann initialisiere ich natürlich nicht (oder benutze die Macros von
Valgrind, um diesem mitzuteilen, daß dieser Bereich ab sofort wieder als
"nicht initialisiert" angesehen zu werden hat.
> die Zugriffe auf nicht initialisierte Daten feststellen koennen,
> waehrend ein Indirektion eines NULL Zeigers nicht immer fehlschlagen muss
Dann mache ich es wohl auch nicht - die Voraussetzung war ja, daß
*(char*)0=0; zu SIGSEGV wird, was sich dann z.B. in einem Debugger abfangen
lässt.
> (auch nicht auf CPUs mit MMU). Unter AIX fuehrt lesen von Adresse 0 zum
> Beispiel nicht zu Fehlern, weil der icc so schnelleren Code erzeugen kann.
Klar, wenn das die MMU nicht kann (warum auch immer) nützt eine
Initialisierung mit 0 nicht so viel, dann könnte man sich noch überlegen,
auf 0xdeadbeef umzusteigen. Das geht dann natürlich nicht mehr via calloc.
> Ein weiterer Nachteil ist, dass Du durch das "Anfassen" des Speichers dafuer
> sorgst, dass das Betriebssystem diesen auch tatsaechlich bereitstellen muss,
> was heftige Plattenaktivitaet (und langsame Programme) nach sich ziehen kann.
Full ACK.
> Mit anderen Worten: Auch hier lohnt sich unportables Programmieren in den
> seltensten Faellen:-)
Man muß abwägen ...
Ich persönlich benutze auch lieber Valgind, als darauf zu hoffen, daß die
einzigen Fehler nichtinitialisierte Zeiger sind ...