Markus Pitha: (C) Was bedeutet (char *)?

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.

--
http://www.apostrophitis.at
Wenn ich ein toller Programmierer währe, könnte ich vieleicht sogar Packete nach einem gewissen Standart kompelieren...
Vieleicht progge ich aber auch eine tolle Gallerie, die dann hoffentlich funzt.
  1. 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.

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

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

      --
      http://www.apostrophitis.at
      Wenn ich ein toller Programmierer währe, könnte ich vieleicht sogar Packete nach einem gewissen Standart kompelieren...
      Vieleicht progge ich aber auch eine tolle Gallerie, die dann hoffentlich funzt.
      1. 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

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

          --
          http://www.apostrophitis.at
          Wenn ich ein toller Programmierer währe, könnte ich vieleicht sogar Packete nach einem gewissen Standart kompelieren...
          Vieleicht progge ich aber auch eine tolle Gallerie, die dann hoffentlich funzt.
          1. 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

    2. 你好 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.

      再见,
       克里斯蒂安

      --
      Neue Hardware eingebaut | Der dritte mir bekannte Block-Nutzer
      If God had a beard, he'd be a UNIX programmer.
      http://wwwtech.de/