www.mikrocontroller.net

Forum: Compiler & IDEs Verständnisproblem Portexpander Code


Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich möchte an ein Schieberegister Daten ausgeben. Jetzt habe ich mir mal 
den Artikel "Portexpander" im Roboternetz Wissen Bereich durchgelesen.

Funktioniert auch alles, bloß kann ich mir als C Einsteiger den Code 
nicht ganz erklären. Das ärgert mich gerade sehr.
Hier mal der Code:


/* SERiell nach PArallel (serpa) via Software */
#include "serpa.h"

/* Array fuer die Daten */
unsigned char serpa[SERPA_SIZE];

void serpa_init ()
{
    /* Verwendete Ports auf OUT */
    MAKE_OUT (PORT_SER);
    MAKE_OUT (PORT_SCK);
    MAKE_OUT (PORT_RCK);

    /* SCR und RCK auf definierten Level HIGH */
    SET (PORT_SCK);
    SET (PORT_RCK);
}

void serpa_out ()
{
    unsigned char anz = SERPA_SIZE;
    unsigned char* serp = serpa+SERPA_SIZE;     //???


// do...while pro Byte einmal -> wird bestimmt durch Array Größe

    do
    {
        unsigned char bits;                       //Anzahl Bits
        unsigned char data = *--serp;       // ??? wohl ein Pointer

        /* 8 Bits pro Byte rausschieben */

        // for schleife pro Bit einmal durchlaufen (8 mal)
        for (bits = 8; bits > 0; bits--)
        {
            CLR (PORT_SER);                    //SER Port auf Masse
            if (data & 0x80)                       //???
            {
                SET (PORT_SER);               //SER Port auf 1
            }

            data <<= 1;                          //???
            /* Strobe an SCK schiebt Daten im Gaensemarsch */
            /* um 1 Position weiter durch alle Schieberegister */
            CLR (PORT_SCK);                 //Takt Port auf Masse
            SET (PORT_SCK);                 //Takt auf 1
        }
    }
    while (--anz > 0);

    /* Strobe an RCK bringt die Daten von den Schieberegistern in die 
Latches, also übernehmen wenn alle Arrays geschoben wurden*/
    CLR (PORT_RCK);
    SET (PORT_RCK);
}




Ich habe den Code mal soweit kommentiert wie ich klar komme. Mit * wird 
wohl ein sog. Pointer benutzt? Ich habe mal im Netz und im GCC Tutorial 
nach Antworten gesucht, leider ohne Erfolg :-(

Ich wäre euch dankbar wenn wir den Code so auseinander nehmen können das 
auch ich es verstehe.

DANKE

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  technikus (Gast)

>Funktioniert auch alles, bloß kann ich mir als C Einsteiger den Code
>nicht ganz erklären.

Dann nimm doch ASM.

AVR-Tutorial: Schieberegister

>    unsigned char* serp = serpa+SERPA_SIZE;     //???

Ein Pointer auf einen char wird initialisiert.

        unsigned char data = *--serp;       // ??? wohl ein Pointer

Verringere den Pointer um 1 und lies den Inhalt. 
Pointerdereferenzierung.

>            if (data & 0x80)                       //???

UND-Verknüfung von data und 0x80, prüft das MSB (Bit 7)

            data <<= 1;                          //???

data wird um eins nach links geschoben

>wohl ein sog. Pointer benutzt? Ich habe mal im Netz und im GCC Tutorial
>nach Antworten gesucht, leider ohne Erfolg :-(

Logisch. C-grundlagen werden hier nur minimal erklärt. Dazu solltest du 
dier ein grundlagenbuch zulegen.

MfG
Falk

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
technikus wrote:

>     unsigned char* serp = serpa+SERPA_SIZE;     //???

fängt von hinten an, also letztes Byte zuerst

>         unsigned char data = *--serp;       // ??? wohl ein Pointer

holt sich das Byte und zählt den Pointer runter

>             if (data & 0x80)                       //???

Testet Bit 7

>             data <<= 1;                          //???

Schiebt nach links, d.h. Bit 0 -> Bit 1, .... Bit 6 -> Bit 7


Peter

Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

danke für die Info!

@Peter:  Fangen wir von hinten an:

>>             data <<= 1;                          //???

>Schiebt nach links, d.h. Bit 0 -> Bit 1, .... Bit 6 -> Bit 7

Werden dann Nullen nachgeschoben? Z.B. erst 10101010, dann <<1, jetzt 
01010100 ?

>>             if (data & 0x80)                       //???

>Testet Bit 7

Das ist mir jetzt klar ( wohl auch schon vorher ;-) )



>>         unsigned char data = *--serp;       // ??? wohl ein Pointer

>holt sich das Byte und zählt den Pointer runter

Ich habe mir mal Grundlagen über C angelesen. Irgendwie kriege ich das 
mit den Pointern nicht hin und stelle mich zu doof an.


>>     unsigned char* serp = serpa+SERPA_SIZE;     //???

>fängt von hinten an, also letztes Byte zuerst

Deswegen verstehe ich das hier auch nicht so recht. serpa ist doch ein 
Array? Was ergibt denn dann serpa+SERPA_SIZE?
Müsste es nicht heißen: unsigned char* serp = serpa[SERPA_SIZE]; ??


Danke für die Hilfe! Vielleicht verstehe ich das ja auch mal. Es wäre 
schön wenn mir jemand Beispiele mit Werten nennt.


Gruß
technikus

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
technikus wrote:
>>>     unsigned char* serp = serpa+SERPA_SIZE;     //???
>
>>fängt von hinten an, also letztes Byte zuerst
>
> Deswegen verstehe ich das hier auch nicht so recht. serpa ist doch ein
> Array? Was ergibt denn dann serpa+SERPA_SIZE?
> Müsste es nicht heißen: unsigned char* serp = serpa[SERPA_SIZE]; ??
Nein, "serpa" ist kein Array, sondern es ist der Name eines Arrays. 
Und in C repräsentiert der Name eines Arrays die Adresse des ersten 
Elements. Der Pointer wird deklariert und gleichzeitig initialisiert, 
und zwar mit der Adresse des letzten Array-Elements von "serpa[]" 
(also der Adresse des ersten Elements plus der Länge des Arrays).

Ist aber verständlich, dass das etwas verwirrend ist. Für EInsteiger 
übersichtlicher könnte man schreiben
unsigned char* serp;
serp = serpa+SERPA_SIZE;
Dann ist auch glasklar, was da wirklich gemacht wird. Ich denke, Dich 
hat das Sternchen irritiert, das ja normalerweise als 
Dereferenzierungsoperator dem Ziel des Pointers einen Wert zuweist. 
Bei der Deklaration sieht das aber anders aus.

Deine Variante würde übrigens dazu führen, dass der Pointer mit dem 
Inhalt der Speicherzelle nach dem letzten Element des Arrays 
initialisiert würde. "serpa[SERPA_SIZE]" existiert schließlich nicht. 
Das letzte Array-Element ist "serpa[SERPA_SIZE-1]".

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes M. wrote:

> Deine Variante würde übrigens dazu führen, dass der Pointer mit dem
> Inhalt der Speicherzelle nach dem letzten Element des Arrays
> initialisiert würde. "serpa[SERPA_SIZE]" existiert schließlich nicht.
> Das letzte Array-Element ist "serpa[SERPA_SIZE-1]".

Ne, ist schon richtig so, das *--serp macht ein pre-decrement, bevor es 
den Inhalt der Adresse liest und alles ist wieder in Butter.

Hinter ein Array zu zeigen ist erlaubt, davor zu zeigen ist undefiniert.


Peter

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> Hinter ein Array zu zeigen ist erlaubt, davor zu zeigen ist undefiniert.
Hab ja auch nichts anderes behauptet. Der von Dir zitierte Text bezog 
sich auf die Zeile
> Müsste es nicht heißen: unsigned char* serp = serpa[SERPA_SIZE]; ??
in technikus' Post von 9:56. Und da kommt auf jeden Fall nicht das raus, 
was eigentlich beabsichtigt ist...

Ich hatte mich allerdings tatsächlich oben verschrieben. Es hätte 
natürlich heißen müssen "Der Pointer wird deklariert und gleichzeitig 
initialisiert,
und zwar mit der Adresse des letzten Array-Elements von "serpa[]"
plus 1 (also der Adresse des ersten Elements plus der Länge des 
Arrays)."
Da kommt dann hinterher das Pre-Decrement ins Spiel.

Und dass man hinter ein Array zeigen darf, heißt ja noch lange nicht, 
dass da was Sinnvolles steht (ich denke, man kann sagen, dass da 
meistens nichts Sinnvolles steht...).

Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Langsam verstehe ich.

Also so?:

do
    {
        unsigned char bits;                       //Anzahl Bits
        unsigned char data = *--serp;             // gucke auf serpa 
[2], [1], [0] (Je Schleifendurchlauf)

        /* 8 Bits pro Byte rausschieben */

        // for schleife pro Bit einmal durchlaufen (8 mal)
        for (bits = 8; bits > 0; bits--)
        {
            CLR (PORT_SER);                    //SER Port auf Masse
            if (data & 0x80)                   // Abfragen ob Bit 7 == 
"1"
            {
                SET (PORT_SER);               //SER Port auf 1
            }

            data <<= 1;                          //Bits nach links 
schieben

            /* Strobe an SCK schiebt Daten im Gaensemarsch */
            /* um 1 Position weiter durch alle Schieberegister */
            CLR (PORT_SCK);                 //Takt Port auf Masse
            SET (PORT_SCK);                 //Takt auf 1
        }
    }
    while (--anz > 0);







Hätte man dann nicht auch eine Zählschleife aufbauen können? Etwa so:




 for (i=2,i>0,i--)
    {
        unsigned char bits;                       //Anzahl Bits
        unsigned char data = serp[i];             // gucke auf serpa 
[2], [1], [0] (Je Schleifendurchlauf)
        /* 8 Bits pro Byte rausschieben */

        // for schleife pro Bit einmal durchlaufen (8 mal)
        for (bits = 8; bits > 0; bits--)
        {
            CLR (PORT_SER);                    //SER Port auf Masse
            if (data & 0x80)                   // Abfragen ob Bit 7 == 
"1"
            {
                SET (PORT_SER);               //SER Port auf 1
            }

            data <<= 1;                          //Bits nach links 
schieben

            /* Strobe an SCK schiebt Daten im Gaensemarsch */
            /* um 1 Position weiter durch alle Schieberegister */
            CLR (PORT_SCK);                 //Takt Port auf Masse
            SET (PORT_SCK);                 //Takt auf 1
        }
    }



Also für 3 Bytes.


Habe ich jetzt richtig verstanden? Danke für Eure Mühe und Geduld.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
technikus wrote:
> Langsam verstehe ich.

das hier:

unsigned char* serp = serpa+SERPA_SIZE;

ist identisch zu

unsigned char* serp = &serpa[SERPA_SIZE];

Aber das sollte mitlerweile schon klar geworden sein.

Graphisch passiert folgendes


   serpa
   +---+---+---+---+---+---+--- ... ---+---+---+
   |   |   |   |   |   |   |           |   |   |
   +---+---+---+---+---+---+--- ... ---+---+---+
                                                 ^
                                                 |
   serp                                          |
   +-------+                                     |
   |  o------------------------------------------+
   +-------+


serp wird also so eingerichtet, dass er auf das Element unmittelbar
hinter dem Array zeigt.

*--serp;   macht dann folgendes:

  --     wie immer wird die rechts davon stehende Variable 
dekrementiert.
         Da es sich aber um einen Pre-Dekrement handelt, ist das
         Ergebnis das Ausdrucks, der Wert der Variablen nach dem
         dekrementieren.

         i = 5;
         j = i--;     i wird um 1 verringert, wird also zu 4.
                      j kriegt aber den Wert 5 (i vor dem Verringern)

         i = 5;
         j = --i;     i wird um 1 verringert, wird also zu 4.
                      j kriegt den Wert von i nach dem Verringern.
                      j wird also ebenfalls zu 4

Bezogen auf den Pointer:
Wenn der Pointer um 1 verringert wird, zeigt er danach auf das
unmittelbar vorhergehende Element

  serpa
   +---+---+---+---+---+---+--- ... ---+---+---+
   |   |   |   |   |   |   |           |   |   |
   +---+---+---+---+---+---+--- ... ---+---+---+
                                             ^
                                             |
   serp                                      |
   +-------+                                 |
   |  o--------------------------------------+
   +-------+

Nachdem serp um 1 verringert wurde, holt dann der Dereferenzier-
operator, der *  in *--serp, den Wert von der Speicheradresse,
die serp nach dem Dekrementieren hat. Also von dort, wo der Pfeil
jetzt hinzeigt.


> Hätte man dann nicht auch eine Zählschleife aufbauen können? Etwa so:

Ja. Hätte man machen können.

  unsigned char Array[SIZE];

  for( i = 0; i < SIZE; ++i )
    j = Array[i];

ist gleichwertig zu

  unsigned char Array[SIZE];
  unsigned char* Address = Array;  // oder &Array[0], was das gleiche
                                   // ausdrückt.

  for( i = 0; i < SIZE; ++i, Address++ )
    j = *Address;

Dasselbe natürlich auch dann, wenn man das Array nicht in
der Reihenfolge 0, 1, 2, 3 sondern genau anders rum abarbeiten
möchte.

Ein guter Compiler formt die erste Form selbsttätig in die
zweite Form um.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Karl heinz Buchegger (kbuchegg) (Moderator)

>  --     wie immer wird die rechts davon stehende Variable
>dekrementiert.
>         Da es sich aber um einen Pre-Dekrement handelt, ist das
>         Ergebnis das Ausdrucks, der Wert der Variablen nach dem
>         dekrementieren.


Sollte man das nicht besser so erklären?

         i = 5;
         j = i--;     // j kriegt Wert von i (5)
                      // i wird *danach* um 1 verringert, wird also zu 4
                      // weil der Operator -- NACH der Variable steht
                      
         i = 5;
         j = --i;     // i wird zuerst um 1 verringert, wird also zu 4
                      // weil der Operator ++ VOR der Variable steht
                      // j kriegt den Wert von i nach dem Verringern, also 4


MfG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:

> Sollte man das nicht besser so erklären?

>
>          i = 5;
>          j = i--;     // j kriegt Wert von i (5)
>                       // i wird *danach* um 1 verringert, wird also zu 4
>                      // weil der Operator -- NACH der Variable steht
> 

Konzeptionell kann man das schon so erklären. Das einzige
Problem dabei ist, dass dir der C-Standard keine Garantie für die
zeitliche Abfolge der Ereignisse gibt. Ob die Zuweisung tatsächlich
vor dem Dekrement erfolgt oder erst danach (natürlich mit dem
alten Wert), ist vom Standard her offen gelassen. Dort heist es einfach
nur: Beim nächsten Sequence Point ist alles erledigt (Sequ.Point ist
in diesem Fall der ;)

Solange du also die Formulierung nicht so wählst, dass der Leser
hier eine zeitliche Reihung zu erkennen glaubt, bin ich durchaus
mit der Formulierung einverstanden.

PS: Mir ist schon klar, dass du im Wesentlichen einen Zusammenhang
zwischen der Position des ++ und der Art der Dekrementierung
bauen willst. Kein Einwand dagegen. Nur wie gesagt: Hüte dich davor,
hier Begriffe wie 'davor' oder 'danach' zu benutzen.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Karl heinz Buchegger (kbuchegg) (Moderator)

>zeitliche Abfolge der Ereignisse gibt. Ob die Zuweisung tatsächlich
>vor dem Dekrement erfolgt oder erst danach (natürlich mit dem
>alten Wert), ist vom Standard her offen gelassen. Dort heist es einfach

Ohje, der C-"Standard". Aber gibt es Compiler, die das verdrehen? Ich 
meine da kann man doch nix optimieren? Jaja, es gibt da dievers 
C-Monsterkonstrukte, aber davon reden wir glaube ich nicht.

MfG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:

> Aber gibt es Compiler, die das verdrehen?

Ich kann mich dunkel an einen Bericht in comp.lang.c++
erinnern, in dem ein Poster einen Compiler hatte der
sowas machte. Ich weiss aber keine Details mehr dazu auswendig.
Ist schon ein paar Jährchen her und das war irgendein esoterischer
Compiler.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist eigentlich definiert was hier heraus kommt?
  a = i+++b;


der Visual C++ 6.0 Compiler inkrementiert i.

Autor: ARM-Fan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>der Visual C++ 6.0 Compiler inkrementiert i.

KEIL C51 auch.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ ARM-Fan (Gast)

>>der Visual C++ 6.0 Compiler inkrementiert i.

>KEIL C51 auch.

Das ist einer der vielen Punkte, die mir bei C auf den Zeiger gehen. 
Solche Operationen, die wieder in einer Operation Parameter verändern. 
Das ist nichts  als ne Stolperfalle ohne jeden realen praktischen 
Nutzen. Viva Pascal!

MfG
Falk

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ARM-Fan wrote:
>>der Visual C++ 6.0 Compiler inkrementiert i.
>
> KEIL C51 auch.


IMHO muss das auch so sein.
Der Compiler muss die längste Kette bilden.
Ein + welches von einem + gefolgt wird, kann nur der
Inkrement Operator sein und nicht 2 'plus' Operationen
hintereinander. (*)

Daher ist
   j = i++5;
auch ein Syntax Error und nicht die verkürzte Schreibweise von
   j = i + +5;

Aber ehrlich:
  a = i+++b;
Wer das so schreibt, sollte mit dem nassen
Fetzen erschlagen werden.
  a = i++ + b;

Jetzt sollte alles klar sein :-)

(*) Das war auch der Grund für eine Syntaxänderung in den
Anfangstagen von C. Ihr alle kennt die +=, -=, *= ...
Operatoren.
In den Anfangstagen hiessen die anders: =+, =-, =* etc.
Das hatte aber den Nachteil, dass bei

   i=-3;

nicht eindeutig erkennbar war, was gemeint ist. Wollte der
Programmierer:

   i =- 3;    also  i = i - 3;
oder
   i = -3;

Der Compiler hat ersteres geparst und anscheinend war des öfteren
letzteres gewollt. Schlussendlich hat man dem Rechnung getragen
und die Zeichen umgedreht.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Karl heinz Buchegger (kbuchegg) (Moderator)

>IMHO muss das auch so sein.
>Der Compiler muss die längste Kette bilden.
>Ein + welches von einem + gefolgt wird, kann nur der
>Inkrement Operator sein und nicht 2 'plus' Operationen. (*)

Und? Es kann auch gemeint sein

  a = i + ++b;

Ist zwar auch wieder krytischer C-Unsinn, aber prinzipiell denkbar.

>Aber ehrlich: Wer das so schreibt, sollte mit dem nassen
>Fetzen erschlagen werden.

>  a = i++ + b;

>Jetzt sollte alles klar sein :-)

Wirklich? Ist es aber nicht so, dass in C Leerzeichen fast KEINE Rolle 
spielen zur Trennung von Konstrukten etc.? Und damit die 
Leerzeichenversion genauso mehrdeutig ist wie die ohne? Und nur 
Klammern das eindeutig definieren?

MfG
Falk

Autor: der mechatroniker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klar spielen Leerzeichen eine Rolle:
inta = 5;
ist ungleich
int a = 5;
obwohl beides gültige Statements sind, im ersten Fall vorherige 
Definition von inta vorausgesetzt.

Somit wird auch klar, warum der Lexer immer das größtmögliche Token 
bilden muß, und warum a+++b auf jeden Fall als a++ +b gelesen werden 
muß.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:
> @ Karl heinz Buchegger (kbuchegg) (Moderator)
>
>>IMHO muss das auch so sein.
>>Der Compiler muss die längste Kette bilden.
>>Ein + welches von einem + gefolgt wird, kann nur der
>>Inkrement Operator sein und nicht 2 'plus' Operationen. (*)
>
> Und? Es kann auch gemeint sein
>
>   a = i + ++b;
>
> Ist zwar auch wieder krytischer C-Unsinn, aber prinzipiell denkbar.

Denkbar schon. Gemeint mag der Programmierer das auch haben.
Aber ausdenken kann ich mir viel, entscheidend ist wie die Sache
normiert wurde. Und normiert ist nun mal: die längste Kette
(das größte Token) zählt.

> Wirklich? Ist es aber nicht so, dass in C Leerzeichen fast KEINE Rolle
> spielen zur Trennung von Konstrukten etc.?

Die Syntax wurde so hingedreht, dass dies tatsächlich oft der
Fall ist. Aber das heist nicht, dass dem in allen Fällen so ist.

> Und damit die Leerzeichenversion genauso mehrdeutig ist wie die ohne?

Äh. nein. Du verwechselst das mit Fortran. In Fortran ist es
tatsächlich eine der ersten Handlungen des Compilers alle Leerzeichen
in einer Anweisung zu entfernen. Zumindest war das so, bis ich so um
1990 aus Fortran ausgestiegen bin. Keine Ahnung ob modernes Fortran
das immer noch so sieht.

In C wird eine Folge von Whitespace ( = Leerzeichen, Tab, CR/LF aber
auch der klassische /* */ Kommentar) auf ein einzelnes Whitespace-
Character reduziert, bevor dann der Parser die einzelnen Wörter aus
dem Quelltext pickt.

Kurz zurück zu Fortran: Es gibt da eine urban Legend, nach der die
NASA eine Sonde blöd verloren hat. Die Geschichte geht so

Der Programmierer wollte schreiben:

    DO 100 I = 1, 20

    ....

100 CONTINUE

(der Teil .... ist in dem Zusammenhang uninteressant)
Statt dessen hat er aber geschrieben:

    DO 100 I = 1. 20

    ....

100 CONTINUE

Falls es jemand nicht sieht: Der Unterschied ist ein . anstelle des ,
in der DO Zeile.

Warum gab das keinen Syntax Error?
Weil der Compiler als erstes alle Spaces entfernt. Damit erhält er

  DO100I=1.20

Da in Fortran standardmässig Variablen implizit definiert werden, hat
der Compiler das als stinknormale Zuweisung aufgefasst: sieht aus
wie eine Zuweisung, ist auch eine. Der Variablen DO100I wird der Wert
1.20 zugewiesen. Wie gesagt werden Variablen implizit definiert, der
Datentyp wird dabei aus dem ersten Buchstaben abgeleitet und 'D' fällt
nun mal in den Bereich für REAL*8, als Gleitkomma. Damit stimmen sogar
die Datentypen links und rechts überein und es gibt noch nicht mal
einen Grund für eine Warnung.

Die ursprüngliche Logik ( das entsprechende Stück würde sich
in C so lesen

    for( i = 1; i < 21; ++i )
      ....
)
war natürlich dahin. Und so hat die NASA durch einen dämlichen
Schreibfehler eine Sonde (die Chronisten sind sich nicht einig
obs zur Venus oder doch zum Merkur gehen sollte) verloren.

Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

damit ich auch bald die letzte Zeile verstanden habe, hier noch einmal 
zurück zu meinem Verständnisproblem:

Im ersten Schleifendurchlauf der Do...while Schleife ist serp ja hier:


 serpa                               letzte Byte
   +---+---+---+---+---+---+--- ... ---+---+---+
   |   |   |   |   |   |   |           |   |   |
   +---+---+---+---+---+---+--- ... ---+---+---+
                                             ^
                                             |
   serp                                      |
   +-------+                                 |
   |  o--------------------------------------+
   +-------+


unsigned char data = *--serp;

Beim nächsten Schleifendurchlauf müsste *--serp ja das hier ergeben:

serpa                           vorletzte Byte
   +---+---+---+---+---+---+--- ... ---+---+---+... ---+---+---+
   |   |   |   |   |   |   |           |   |   |               |
   +---+---+---+---+---+---+--- ... ---+---+---+... ---+---+---+
                                             ^
                                             |
   serp                                      |
   +-------+                                 |
   |  o--------------------------------------+
   +-------+


Bedeutet also *--serp dass der Zeiger um Acht Bit nach links verschoben 
wird? Also richtung kleineres Byte.


Gruß
technikus

Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder ist in der grafischen Darstellung pro Kasten ein char (also ein 
Byte) gemeint?


serpa
   +---+---+---+---+---+---+--- ... ---+---+---+
   |   |   |   |   |   |   |        [7]|[8]|[8]|
   +---+---+---+---+---+---+--- ... ---+---+---+
                                             ^
                                             |
   serp                                      |
   +-------+                                 |
   |  o--------------------------------------+
   +-------+


Also wird bei *--serp immer ein Byte weiter nach links gesprungen, oder 
vielmehr drauf gezeigt? Und das Abbild nach data "kopiert" ?

Ich danke euch vielmals für die Mühe! Mann ist C für einen Einsteiger 
kryptisch ;-)

Jetzt weiß ich warum die "Laien" Bascom einsetzen - einfach Shiftout und 
schwups ist das Byte draussen im Schieberegister.



Gruß
technikus

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ technikus (Gast)

>Bedeutet also *--serp dass der Zeiger um Acht Bit nach links verschoben
>wird?

NEIN! Er wird um EINS verringert.

> Also richtung kleineres Byte.

Ja, aber der zeiger zeigt auf Bytes, nicht auf Bits.

>Oder ist in der grafischen Darstellung pro Kasten ein char (also ein
>Byte) gemeint?

Ja.

>Also wird bei *--serp immer ein Byte weiter nach links gesprungen, oder
>vielmehr drauf gezeigt? Und das Abbild nach data "kopiert" ?

Nicht das Abbild. Das Bytes aus dem RAM, worauf er zeigt. Das Ziel des 
Zeigers.

>Ich danke euch vielmals für die Mühe! Mann ist C für einen Einsteiger
>kryptisch ;-)

Meine Rede.

MfG
Falk

Autor: technikus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
O.K. Dann sind doch endlich alle Unklarheiten aus dem Weg geräumt.

Danke!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falk Brunner wrote:

>>Oder ist in der grafischen Darstellung pro Kasten ein char (also ein
>>Byte) gemeint?
>
> Ja.


@technikus

Aber nur in diesem Fall. Und das auch nur, weil das Array vom
Typ unsigned char ist und daher auch der Pointer ein Pointer
auf unsigned char ist.

Wenn das beispielsweise ein double Array wäre, dann würde ein
Kästchen einen double repräsentieren und der Pointer müsste
klarerweise auch ein Pointer auf double sein

   double Array[XYZ];
   double* pPtr = Array;

Pointer Arithmetik, und genau darum handelt es sich ja wenn man
einen Pointer erhöht oder verringert, berücksichtigt immer den
Basistyp des Pointers. Bei einem 'unsigned char * pPtr' ist
der Basistyp 'unsigned char'. Bei einem 'double * pPtr' ist der
Basistyp 'double' und bei einem 'struct abc * pPtr' ist der Basistyp
'struct abc'. Bei jeglicher Pointerarithmetik wird immer die sizeof
des Basistyps automatisch mit eingerechnet.

Sei T irgendein beliebiger Datentyp und sei pPtr ein Pointer darauf:
      T* pPtr;
dann ist
      pPtr + offset;
identisch zu
      ((unsigned char*)pPtr) + offset * sizeof(T);

Dies ist notwendig, da in C Arrayzugriffe in Form von Zugriffen
über Pointer definiert sind:

     T Array[XYZ];

     Array[offset] <==>  *( Array + offset );

und durch die Art und Weise, wie Pointer Arithmetik definiert ist,
ist damit auch sichergestellt, dass die Größe des Basistyps des
Arrays korrekt in die Arrayindizierung einfliest.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.