(C) Was bedeutet (char *)?
Markus Pitha
- sonstiges
Hi,
kurze Frage:
Wenn ich folgenden Ausdruck hernehme:
c = (char *) malloc ( 128*128 * 2*sizeof (char) );
Was bedeutet dann char in Klammer mit dem Stern? (char) wäre klarerweise eine Typumwandlung, aber (char *)? Oft habe ich auch schon Konstrukte wie...
*(Uint32 *)pixaddr = pixel;
...gesehen. Kann mir *(Unit32 *) jemand erklären?
Markus.
Hi,
der Stern kennzeichnet einen Pointer. Malloc gibt ja nur einen Pointer auf den Speicerbedreich zurüch, deshalb muss der Cast auch ein Pointer sein.
Hallo Markus,
c = (char *) malloc ( 128*128 * 2*sizeof (char) );
du hast doch schon ganz richtig erkannt, dass ein Datentyp in Klammern vor einem Ausdruck eine Typumwandlung, auch als type cast bekannt, bedeutet. Und der Stern in Verbindung mit einem Datentyp bedeutet doch einen Zeiger auf den entsprechenden Typ, wie z.B. in der Deklaration
char *c;
Was bedeutet dann char in Klammer mit dem Stern?
Logischerweise einen type cast in einen Zeiger auf char. Wobei das in Verbindung mit malloc() recht sinnfrei ist, denn malloc() liefert sowieso einen untypisierten Zeiger, also void*, der zu allen anderen Zeigertypen zuweisungskompatibel ist. Ein type cast nach dem Aufruf von malloc() erfüllt also keinen sinnvollen Zweck, sondern erschwert nur das Lesen.
*(Uint32 *)pixaddr = pixel;
"Nimm den aktuellen Wert von pixaddr, tu so als sei es ein Zeiger auf Uint, und weise dem Uint, auf den es dann zeigen würde, den Wert von pixel zu."
Schönen Abend noch,
Martin
Hallo,
danke, der erste Teil ist mir jetzt klar.
*(Uint32 *)pixaddr = pixel;
"Nimm den aktuellen Wert von pixaddr, tu so als sei es ein Zeiger auf Uint, und weise dem Uint, auf den es dann zeigen würde, den Wert von pixel zu."
Ist mir im Grunde genommen auch klar, aber dennoch weiß ich nicht ganz, was es im Code eigentlich bewirkt, wenn die dazugehörende Funktion, die ein Pixel am Bildschirm zeichnet, folgendermaßen aussieht:
void setpixel(SDL_Surface *surface, int x, int y, Uint8 R, Uint8 G, Uint8 B) {
int bbp = surface->format->BytesPerPixel;
Uint32 pixel = SDL_MapRGB(surface->format, R, G, B);
//pixaddr ist die Adresse zum Pixel, das wir setzen wollen
Uint8 *pixaddr = (Uint8 *)surface->pixels + y *surface->pitch + x * bbp;
switch(bbp) {
case 1:
*pixaddr = pixel;
break;
case 2:
*(Uint16 *)pixaddr = pixel;
break;
case 3: // langsamer 24-Bit-Modus, selten verwendet
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
pixaddr[0] = (pixel >> 16) & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[2] = pixel & 0xff;
} else {
pixaddr[2] = pixel & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[0] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)pixaddr = pixel;
break;
}
}
Desweiteren glaube ich sogar, dass die Verzweigung case 3 fehlerhaft ist. Es wird zwischen big endian, und offensichtlich little endian unterschieden, aber dennoch wird den Indizes 2 x dasselbe zugewiesen. Müsste die else-Verzweigung nicht so aussehen?
pixaddr[0] = pixel & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[2] = (pixel >> 16) & 0xff;
Markus.
Hallo Markus,
Ist mir im Grunde genommen auch klar, aber dennoch weiß ich nicht ganz, was es im Code eigentlich bewirkt, wenn die dazugehörende Funktion, die ein Pixel am Bildschirm zeichnet, folgendermaßen aussieht:
void setpixel(SDL_Surface *surface, int x, int y, Uint8 R, Uint8 G, Uint8 B)
Scheint eine Funktion zu sein, die ein Pixel auf einen virtuellen Bildschirm pinselt.
int bbp = surface->format->BytesPerPixel;
Uint32 pixel = SDL_MapRGB(surface->format, R, G, B);
//pixaddr ist die Adresse zum Pixel, das wir setzen wollen
Uint8 *pixaddr = (Uint8 *)surface->pixels + y *surface->pitch + x * bbp;
Okay, pixaddr ist ein Zeiger auf die Position in den Bitmap-Daten, die dem gewünschten Pixel (X,Y) entspricht, und pixel enthält nun den zur gewünschten Farbe (R,G,B) gehörenden Farbwert.
switch (bbp) {
{ case 1:
*pixaddr = pixel;
break;case 2:
*(Uint16 *)pixaddr = pixel;
break;case 4:
*(Uint32 *)pixaddr = pixel;
break;
Diese drei Fälle (1/2/4 Bytes pro Pixel) sind trivial. Es wird entweder ein Byte (pixaddr ist ja als Uint8 deklariert) oder ein Wort (Uint16) oder ein Doppelwort (Uint32) kopiert.
Desweiteren glaube ich sogar, dass die Verzweigung case 3 fehlerhaft ist. Es wird zwischen big endian, und offensichtlich little endian unterschieden, aber dennoch wird den Indizes 2 x dasselbe zugewiesen.
case 3: // langsamer 24-Bit-Modus, selten verwendet
if (SDL_BYTEORDER==SDL_BIG_ENDIAN)
{ pixaddr[0] = (pixel >> 16) & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[2] = pixel & 0xff;
}
else
{ pixaddr[2] = pixel & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[0] = (pixel >> 16) & 0xff;
}
Gut beobachtet. Dieser Abschnitt scheint tatsächlich fehlerhaft zu sein, es wird in beiden Zweigen dem Motorola-Format (MSB auf niedrigster Adresse) entsprechend kopiert. Auf einer Intel-CPU (LSB zuerst) käme Unsinn raus. In diesem Anwendungsbeispiel würden wohl die Rot- und Blaukanäle vertauscht.
Schönen Tag noch,
Martin
Hi,
Ist mir im Grunde genommen auch klar, aber dennoch weiß ich nicht ganz, was es im Code eigentlich bewirkt, wenn die dazugehörende Funktion, die ein Pixel am Bildschirm zeichnet, folgendermaßen aussieht:
Ich weiss ja,das es Dich beim Verstaendnis nicht viel weiterbringen wird, aber wer hat das verbrochen und warum?
void setpixel(SDL_Surface *surface, int x, int y, Uint8 R, Uint8 G, Uint8 B) {
int bbp = surface->format->BytesPerPixel;
Was passiert wenn surface->format->BytesPerPixel nicht oder falsch belegt ist?
Wird das vorher geprueft?
> ~~~c
> Uint32 pixel = SDL_MapRGB(surface->format, R, G, B);
>
Wo wird der Rueckgabewert von SDL_MapRGB() geprueft oder gibt diese FUnktion zwingend etwas gueltiges zurueck?
//pixaddr ist die Adresse zum Pixel, das wir setzen wollen
Uint8 *pixaddr = (Uint8 *)surface->pixels + y *surface->pitch + x * bbp;
Die ersten beiden sind Sternchen sind Zeichen fuer Pointer, die beiden anderen sind Multplikationen. Wenn sowas oefter vorkommt waere es der Lesbarkeit dienlich den Praeprozessor zu beschaeftigen. Is aber natuerlich reine Geschmacksache.
Es muss zwar nicht, kann aber hier zwei Probleme geben: einmal ein Vorzeichenproblem (wenn Uint8 "8 Bit langer vorzeichenloser Integer" bedeutet) wenn 'bbp' kleiner als 0 ist und zum zweitem, wenn bbp gleich Null ist. Bei letzterem wird auch der Pointer "genullt", ein spaetere Zuweisung ist dann nicht mehr moeglich.
> ~~~c
> switch(bbp) {
> }
>
Auch wenn hier wahrscheinlich nicht noetig, dennoch fehlt ein default Zweig im switch().
Desweiteren glaube ich sogar, dass die Verzweigung case 3 fehlerhaft ist. Es wird zwischen big endian, und offensichtlich little endian unterschieden, aber dennoch wird den Indizes 2 x dasselbe zugewiesen. Müsste die else-Verzweigung nicht so aussehen?
pixaddr[0] = pixel & 0xff;
pixaddr[1] = (pixel >> 8) & 0xff;
pixaddr[2] = (pixel >> 16) & 0xff;
Das koennte moeglich sein, allerdings ist hin und wieder auch die Reihenfolge der Zuweisung wichtig, was aber dann auch in fast allen Faellen ein deutliches Zeichen von Murks ist. Duerfte aber wirklich nur ein C&P Fehler gewesen sein: Zeilen per C&P umsortiert, aber vergessen die Indizes zu aendern.
Allerdings und nicht zuletzt deshalb ist jeglicher endianessabhaeniger Code auch dringend zu vermeiden!
Erst wenn Du das beherrschst kannst Du es auch anders machen ;-)
Zuletzt noch: bitte beschaeftige Dich mal ausfuehrlich mit Pointern. Das muss im Schlaf sitzen, wenn Du Dich naeher mit C beschaeftigen moechtest.
Ich kann auch nicht oft genug die [C-FAQ](http://www.eskimo.com/~scs/C-faq/top.html) an's Herz legen.
so short
Christoph Zurnieden
Hi,
//pixaddr ist die Adresse zum Pixel, das wir setzen wollen
Uint8 *pixaddr = (Uint8 *)surface->pixels + y *surface->pitch + x * bbp;
>
> [... ]zum zweitem, wenn bbp gleich Null ist. Bei letzterem wird auch der Pointer "genullt", ein spaetere Zuweisung ist dann nicht mehr moeglich.
Na, so ganz nuechtern scheine ich aber noch nicht wieder zu sein ;-)
so short
Christoph Zurnieden
Hi,
Ich weiss ja,das es Dich beim Verstaendnis nicht viel weiterbringen wird, aber wer hat das verbrochen und warum?
Ich glaube, dass ich dieses Beispiel aus einem Anfänger-SDL-Tutorial habe, wobei ich hier aunahmsweise mal auf die Richtigkeit vertraute, da ich über speicherinterne Prozesse, vor allem bezüglich Grafiken, noch nicht so viel weiß.
void setpixel(SDL_Surface *surface, int x, int y, Uint8 R, Uint8 G, Uint8 B) {
int bbp = surface->format->BytesPerPixel;
>
> Was passiert wenn surface->format->BytesPerPixel nicht oder falsch belegt ist?
> Wird das vorher geprueft?
Nein, außer es basiert irgendwie auf den selben Grundlagen wie die Initialisierung von SDL. Ich denke mir einfach, dass es gar nicht nicht belegt sein kann, da sonst SDL nicht initialisiert werden könnte, und das Programm gar nicht ausgeführt werden könnte.
> > ~~~c
> > pixaddr[0] = pixel & 0xff;
> > pixaddr[1] = (pixel >> 8) & 0xff;
> > pixaddr[2] = (pixel >> 16) & 0xff;
> >
Das koennte moeglich sein, allerdings ist hin und wieder auch die Reihenfolge der Zuweisung wichtig, was aber dann auch in fast allen Faellen ein deutliches Zeichen von Murks ist. Duerfte aber wirklich nur ein C&P Fehler gewesen sein: Zeilen per C&P umsortiert, aber vergessen die Indizes zu aendern.
Scheint mir auch so.
Allerdings und nicht zuletzt deshalb ist jeglicher endianessabhaeniger Code auch dringend zu vermeiden!
Erst wenn Du das beherrschst kannst Du es auch anders machen ;-)
Wie würde man ihn ansatzweise vermeiden?
Zuletzt noch: bitte beschaeftige Dich mal ausfuehrlich mit Pointern. Das muss im Schlaf sitzen, wenn Du Dich naeher mit C beschaeftigen moechtest.
Das ist klar. Mit Pointern kenne ich mich auch eigentlich relativ gut aus, aber offensichtlich wurde die Thematik, einen Zeiger auf einen umgewandelten Typ, von meinem Buch entweder irgendwie verschluckt, oder ich habe die Beschreibung bisher nicht gefunden, was ich aber nicht glaube.
Markus.
Hi,
Wird das vorher geprueft?
Nein, außer es basiert irgendwie auf den selben Grundlagen wie die Initialisierung von SDL. Ich denke mir einfach, dass es gar nicht nicht belegt sein kann, da sonst SDL nicht initialisiert werden könnte, und das Programm gar nicht ausgeführt werden könnte.
Wie ich gern an solcher Stelle zu bemerken pflege: wir sind hier nicht in einem theologischem Seminar, Programmierung hat nichts mit Glauben zu tun.
Du hast die Quellen vor Dir liegen, schau also nach, wenn Du unsicher bist. Du musst Dich auch nicht mit 'find' und 'grep' und aehnlichem abquaelen, gute Editionshilfen, um den Begriff IDE hier zu vermeiden, sollten entprechendes Werkzeug vorhalten.
Allerdings und nicht zuletzt deshalb ist jeglicher endianessabhaeniger Code auch dringend zu vermeiden!
Erst wenn Du das beherrschst kannst Du es auch anders machen ;-)Wie würde man ihn ansatzweise vermeiden?
Gar nicht.
Wenn vermeiden, dann auch gleich richtig, also vollstaendig.
In diesem Fall werden drei Werte in ein Integerarray kopiert. Die Methodik zur Errechnng des Wertes, das Rechtsschieben ist jedoch endianabhaengig, da sie in einem einzigem Integer versteckt wurden. Also waere es nicht unguenstig, die drei Werte von Anfang an auf drei getrennte Integer zu verteilen. Ein struct wuerde sich dafuer z.B. anbieten. Die Geschwindigkeitseinbussen sind ueblicherweise vernachlaessigbar.
Sind sie es nicht, wird auch besser per Praeprozessor entschieden welcher Codeteil kompiliert werden soll, nicht dynamisch. Die Endianessabfrage aus dem Beispielcode benoetigt schon mehr Takte, als durch die Verwendung eines structs verloren gehen koennten.
Zuletzt noch: bitte beschaeftige Dich mal ausfuehrlich mit Pointern. Das muss im Schlaf sitzen, wenn Du Dich naeher mit C beschaeftigen moechtest.
Das ist klar. Mit Pointern kenne ich mich auch eigentlich relativ gut aus, aber offensichtlich wurde die Thematik, einen Zeiger auf einen umgewandelten Typ, von meinem Buch entweder irgendwie verschluckt, oder ich habe die Beschreibung bisher nicht gefunden, was ich aber nicht glaube.
Dann lass Dir das Geld zurueckgeben ;-)
Kennst Du eigentlich Dave Marshall?
so short
Christoph Zurnieden
你好 Der,
Wobei das in Verbindung mit malloc() recht sinnfrei ist, denn malloc()
liefert sowieso einen untypisierten Zeiger, also void*, der zu allen
anderen Zeigertypen zuweisungskompatibel ist.
Theoretisch richtig, praktisch geben hier einige (ältere) Compiler Warnings
aus, man hat sogar schon davon gehört, dass es Compiler angibt, die das als
Fehler ansehen.
再见,
克里斯蒂安