Forum: Compiler & IDEs Verschiedene Fragen zu Zeigern


von Paul H. (powl)


Lesenswert?

Hi!

Der Datentyp eines Zeigers soll ja dem des Wertes entsprechen, auf den 
er zeigt. Welchen Datentyp muss ich nun verwenden wenn ich ein Array 
habe in dem wiederum Zeiger drin sind?

char *zeiger1, *zeiger2;
const datentyp *zeigerarray[] = { zeiger1, zeiger2 };

Und wie verhält es sich bei mehrdimensionalen Arrays? Angenommen ich 
habe zwei Dimensionen. Dann ist die erste Dimension ein Zeiger-array, 
deren Zeiger wieder auf andere Zeiger-Arrays zeigen. Und wann und wo 
muss man casten?

kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger 
und Variablen?

unsigned char variable = 128;
const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };

Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?

datentyp *port;
port = &PORTB;

einfach nur so?

lg PoWl

von Rolf Magnus (Gast)


Lesenswert?

> Welchen Datentyp muss ich nun verwenden wenn ich ein Array
> habe in dem wiederum Zeiger drin sind?

Zeiger auf Arrays gibt es zwar, werden aber eher selten verwendet. Man 
nimmt stattdessen einen Zeiger auf dessen erstes Element, in diesem Fall 
also einen Zeiger auf einen Zeiger.

> char *zeiger1, *zeiger2;
> const datentyp *zeigerarray[] = { zeiger1, zeiger2 };

Wenn das ein Zeiger auf ein Array werden sollte, auf welches Array 
sollte er denn zeigen? Das dahinter ist nur ein Initialisierer. Du 
willst also vermutlich gar keinen Zeiger, sondern einfach nur ein Array:

const char *zeigerarray[] = { zeiger1, zeiger2 };
(Array aus Zeigern auf const char).

> Und wie verhält es sich bei mehrdimensionalen Arrays?

Eigentlich gibt es in C gar keine mehrdimensionalen Arrays.

> Angenommen ich habe zwei Dimensionen. Dann ist die erste Dimension ein
> Zeiger-array, deren Zeiger wieder auf andere Zeiger-Arrays zeigen.

Kommt drauf an. So kann man ein mehrdimensionales Array in C nachbilden. 
Vor allem bei dynamischer Allkokierung, bei der erst zur Laufzeit die 
Dimensionen bekannt sind, muß man das so machen. Bei statischen Arrays 
oder bei dynamischen, wo nur eine Dimension variabel ist, nimmt man der 
Einfachheit halber eher direkt Arrays aus Arrays.

> Und wann und wo muss man casten?

Nirgends.

> kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger
> und Variablen?

Zeiger sind auch Variablen (sofern sie einen Namen haben).

> unsigned char variable = 128;
> const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };

Nein, geht nicht. Du kannst einen generischen Zeiger verwenden, also 
einen Zeiger auf void. Der kann auf beliebige Sachen zeigen. Allerdings 
mußt du dir dann selber merken, welcher Typ das war und vor dem Zugriff 
in diesen Typ casten.

> Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?

Dann muß der Zeiger-Zieltyp eben derselbe sein wie der deines Ports.

> datentyp *port;
> port = &PORTB;

Ich rate jetzt einfach mal, daß es sich vermutlich um einen AVR handeln 
soll. Dann wäre der Typ uint8_t.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Paul Hamacher wrote:
> Der Datentyp eines Zeigers soll ja dem des Wertes entsprechen, auf den
> er zeigt. Welchen Datentyp muss ich nun verwenden wenn ich ein Array
> habe in dem wiederum Zeiger drin sind?
>
> char *zeiger1, *zeiger2;
> const datentyp *zeigerarray[] = { zeiger1, zeiger2 };
1
char *zeigerarray[];
> Und wie verhält es sich bei mehrdimensionalen Arrays?
1
char *za[][10];

> Angenommen ich
> habe zwei Dimensionen. Dann ist die erste Dimension ein Zeiger-array,
> deren Zeiger wieder auf andere Zeiger-Arrays zeigen.

Nein, es wird zwar immer wieder behauptet, dass mehrdimensionale 
C-Arrays eigentlich eindimensionale Arrays mit Pointern auf die 
weiterend Dimensionen sind, aber das stimmt einfach nicht.

> Und wann und wo
> muss man casten?

Gar nicht.

> kann ich in ein array auch mehrere Datentypen einspeichern? Z.b. Zeiger
> und Variablen?

Mit Tricks ja. Als Anfänger solltest du das lassen, du blickst ja schon 
bei den Grundlagen nicht durch.

> unsigned char variable = 128;
> const datentyp multiarray[] = { *zeiger1, *zeiger2, variable };
1
typedef union {
2
   unsigned char c;
3
   char *s;
4
} Durcheinander;
5
6
Durcheinander array[] = { {.s = *zeiger1 }, {.s = *zeiger2 }, {.c = variable }};
> Wie verhält sich das ganze, wenn ich auf einen Port zeigen möchte?

Lass es sein.

avr-gcc / avr-lib: Mit entsprechenden Makros bekommst du eine 
Portadresse in einem unit16_t Datentypen, also einem 16-Bit Integer, und 
nicht etwa einem Pointer. Anfangen kannst du damit wenig.

> datentyp *port;
> port = &PORTB;
1
uint16_t port = _SFR_ADDR(PORTB);

> einfach nur so?

Nein.

von Rolf Magnus (Gast)


Lesenswert?

> avr-gcc / avr-lib: Mit entsprechenden Makros bekommst du eine
> Portadresse in einem unit16_t Datentypen, also einem 16-Bit Integer,
> und nicht etwa einem Pointer. Anfangen kannst du damit wenig.

Deswegen verstehe ich auch nicht, warum du das schreibst.

>> datentyp *port;
>> port = &PORTB;
> uint16_t port = _SFR_ADDR(PORTB);

Dann hat er aber eben keinen Zeiger.

>> einfach nur so?
>
> Nein.

Doch, für datentyp = volatile uint8_t funktioniert das prima (das 
volatile hatte ich in meinem Vorposting vergessen):

volatile uint8_t *port;
port = &PORTB;

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

> Deswegen verstehe ich auch nicht, warum du das schreibst.

Damit Leute die was lernen wollen es können. Der Rest geht mir am Arsch 
vorbei.

von Paul H. (powl)


Lesenswert?

Danke für die Antworten! Ist mir schon alles etwas schlüssiger. Diese 
union-Geschichte verwirrt mich allerdings noch und ich frage mich wo man 
lernt so etwas anzuwenden? In meinen C-Büchern und im GCC-Tutorial 
stehen vielleicht die Grundlagen aber nicht wie man damit in der Form 
umgehen kann.

Mein Problem ist: Ich habe einen PORT, ein DDR und zwei unsigned chars. 
Und das ganze Paket 3 mal.

Mit dem Inhalt einer Variablen möchte ich nun bestimmen welches dieser 
Pakete in meiner Funktion verwendet werden soll.

Quasi folgendermaßen:

[0][0] PORTB
[0][1] DDRB
[0][2] 394
[0][3] 102

[1][0] PORTD
[1][1] DDRD
... usw..

und jetzt mit einer variable so in der Art drauf zugreifen:
array[variable][0] = 0xFF;

Nachdem was ihr geschrieben habt wird das also unheimlich kompliziert? 
Also lieber die PORTs, DDRs und Zahlen in jeweils ein separates array 
speichern?

[0] = PORTB
[1] = PORTD
...

[0] = DDRB
[1] = DDRD
...

[0] = 394
[1] = 102
... usw...

lg PoWl

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:
> Also lieber die PORTs, DDRs und Zahlen in jeweils ein separates array
> speichern?

Nein, nicht Arrays.

Das Sprachelement, welches dir erlaubt unterschiedliche Datentypen
zu gruppieren, ist eine Struktur. Selbst dann, wenn alle Struktur-
elemente den gleichen Typ haben, aber nur einen logischen
Zusammenhzang, fährst du mit einer Struktur immer besser.

Also bastelst du dir erst mal eine Struktur, die dir einen solchen
Datensatz beschreibt.

EIn Datenelement ist bei die Zusammenstellung aus Port, DDR, Wert1
und Wert2 (Für Wert1 und Wert2 solltest du noch sinnvolle Namen
benutzen, ich konnte aus dem Zusammenhang nicht erkennen wofür
die beiden Zahlen stehen)
1
struct PortDaten
2
{
3
  volatile uint8_t * Port;
4
  volatile uint8_t * Ddr;
5
  uint16_t           Wert1;
6
  uint16_t           Wert2;
7
};

Damit hast du jetzt eine Möglichkeit, wie du einen Datensatz
{ PORTB, DDRB, 314, 104 }
als eine Einheit betrachten kannst. Wenn du mit einem struct PortDaten
um dich wirfst, dann ist das immer diese Einheit aus diesen 4 Werten.

Und nun möchtest du ein Array aus derartigen Strukturen bauen. Du
willst also viele derartige Portdaten haben
1
struct PortDaten Daten[100];

Der Wert1 vom 1. Element (bei 0 angefangen zu zählen, weil es
ja ein Array ist), kriegt zb. so seinen Wert
1
    Daten[1].Wert1 = 200;

Und um den Port im 5. Element zu setzen, schreibst du
1
    Daten[5].Port = PORTB;

Und um die Daten des 6. ELements an ihrem Port auszugeben
würde man schreiben
1
    Daten[6].Port = Daten[6].Wert2;

ABer sowas würde man wahrscheinlich schon mal in einer Funktion
verstecken. Die Funktion bekommt nur den Datensatz übergeben
und erledigt dann das Setzen des DDR und die Ausgabe an den Port
1
void OutData( struct PortDaten Data )
2
{
3
  Data.Ddr  = Wert1;
4
  Data.Port = Wert2;
5
}

Aufgerufen wird es dann so
1
    ....
2
3
    OutData( Daten[7] );

Nicht alles ist ein Array. Arrays sind nur dann angebracht,
wenn du gleichartige Dinge speichern musst. Gleichartig heißt
in dem Fall weniger 'vom gleichen Datentyp' sondern mehr:
'Das sind logisch gesehen gleichartige Dinge'. Eine Portbezeichnung,
das DDR Register sowie die auszugebenden Werte sind logisch gesehen
keine gleichartigen Dinge. Also ist ein Array nicht angebracht,
sondern man fasst diese Elemente zunächst zu einer Struktur
zusammen und schafft sich so logisch gleichartige Sachen.

Ein anderes Beispiel: Du willst Daten (Mehrzahl von Datum, also
3. März, 5. April, etc.) speichern. Nun: Tag, Monat, Jahr sind alles
nur Zahlen. Heißt das jetzt, daß du dir nur ein Zahlenarray
machst? Nein! Tag, Monat und Jahr sind logisch gesehen verschiedene
Dinge. Also mache ich mir zunächst mal einen neuen Datentyp dafür,
der mir diese 3 Dinge zusammengruppiert. Das macht auch deshalb Sinn,
weil ich ja in erster Linie mit Daten um mich werfen will und nicht
mit Tagen, Monaten oder Jahren.
1
struct Datum
2
{
3
  uint8_t   Tag;
4
  uint8_t   Monat;
5
  uint16_t  Jahr;
6
};

Und das ist dann meine Basiseinheit, mit der ich weiter arbeite.

Anderes Beispiel. Du willst Personendaten speichern. Jede Person
hat einen Vornamen und einen Nachnamen. Sind doch alles nur
Strings. Mache ich mir deshalb ein Stringarray? Nein, natürlich
nicht! Ich mache mir zunächst eine Gruppierung, die mir Vorname
und Nachname gruppiert und zu einer Person zusammenfasst
1
struct Person {
2
  char   Vorname[30];
3
  char   Nachname[30];
4
};

Jetzt hat die Person ev. noch einen Geburtstag. Holla, da
kann ich ja den struct Datum von da oben benutzen. Der macht
doch genau das: Ein Tagesdatum speichern.
1
struct Person {
2
  char         Vorname[30];
3
  char         Nachname[30];
4
  struct Datum Geburtstag;
5
};

Jetzt will ich vielleicht die Werte für Peter speichern, der bei
mir als Arbeiter angestellt ist. (Das wird man zwar so nicht machen,
aber es ist ja nur ein Beispiel um die Zugrifssyntax zu zeigen)
1
  struct Person Arbeiter;
2
3
  strcpy( Arbeiter.Vorname, "Peter" );
4
  strcpy( Arbeiter.Nachname, "Mustermann" );
5
  Arbeiter.Geburtstag.Tag = 22;
6
  Arbeiter.Geburtstag.Monat = 3;
7
  Arbeiter.Geburtstag.Jahr = 1963;

oder ich bau mir Funktionen, die die Werte wieder ausgeben:
1
void OutDate( struct Datum Date )
2
{
3
  // hier könnte man mehr Logik hineinstecken, die zb. den
4
  // Monat in Textform ausgibt
5
  printf( "%d. %d. %d", Date.Tag, Date.Monat, Date.Jahr );
6
}
7
8
void OutPerson( struct Person Pers )
9
{
10
  printf( "%s %s\n", Pers.Vorname, Pers.Nachname );
11
  printf( "Geburtstag: " );
12
  OutDate( Pers.Geburtstag );
13
  printf( "\n" );
14
}

Um, dann ev. ein Array von max. 100 Personen zu haben, die
ich in einer Personallistenfunktion ausgeben will
1
struct Person[100];
2
int NrPersonen;
3
4
...
5
6
void Personalliste()
7
{
8
  int i;
9
10
  for( i = 0; i < NrPersonen; ++i )
11
    OutPerson( Personen[i] );
12
}

Wenn du Literatur hast, dann studier mal das entsprechende
Kapitel über Strukturen und auch wie man verhindern kann,
dass ständig Datensätze bei der Funktionsübergabe kopiert
werden müssen.

von Paul H. (powl)


Lesenswert?

Ich bin erstaunt, das sollte man ins Tutorial verlinken, ein riesiges 
dankeschön, dass du dir so eine Mühe gemacht hast! Im Ernst, hast du dir 
mal überlegt Bücher zu schreiben? Ich könnte dich uneingeschränkt 
empfehlen.

Hab mir erst die hälfte durchgelesen und werde jetzt weiterlesen.

zu meiner verteidigung muss ich sagen: Ich bin PHP und JavaScript 
gewohnt. In PHP kann man recht schlampig mit Arrays umgehen und in 
JavaScript ist soweit alles ein riesiger Objekt/Arrayhaufen.

lg
Paul Hamacher

von Simon K. (simon) Benutzerseite


Lesenswert?

Der Hauptunterschied ist wohl, dass sowohl Javascript als auch PHP 
Typeless sind. Also die Variablen dort keine Datentypen besitzen.

von Paul H. (powl)


Lesenswert?

Nochmals danke für die ausführliche Beschreibung!

Habe das eben mal ausprobiert:
1
struct light
2
{
3
  volatile uint8_t *port;
4
  volatile uint8_t *ddr;
5
  const uint8_t portpin;
6
  uint8_t pwm_value;
7
};
8
9
struct light channels[3];

Für folgende Zeile krige ich jedoch eine Fehlermeldung:
1
channels[0].pwm_value = 10;

error: expected '=', ',', ';', 'asm' or '__attribute__' before '.' token

Hab ich was falsch geschrieben?

Habe hier übrigens ein C-Buch in dem die Struct-Schreibweise auch so 
drinsteht. In meinem µC-Buch im C-Abschnitt wird das allerdings 
irgendwie anders gemacht:
1
struct {
2
  uint8_t bla;
3
  uint8_t blub;
4
} variable;

damit hat man dann scheinbar gleich ne variable definiert:
variable.bla = 10;

oder
1
typedef struct {
2
  uint8_t bla;
3
  uint8_t blub;
4
} str_typ;
5
6
str_typ variable;
7
variable.bla = 10;

ist das das gleiche wie
1
struct str_typ {
2
 ...
3
};
4
5
struct str_typ variable;

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:

> ist das das gleiche wie

Im Prinzip ja.

Mittels
1
struct foo
2
{
3
  int bla;
4
};

legst du nur fest, wie so eine Struktur aussieht.
Um eine Variable davon anzulegen, schreibst du
1
struct foo myVar;

Das ist die 'Langform'.
Das kann man jetzt auch etwas verkürzen und zusammenziehen.
1
struct foo
2
{
3
  int bla;
4
} myVar;

definiert in einem Zug, wie die Struktur aussieht und definiert
gleichzeitig eine Variable dieses Typs.

Muss man jetzt viel mit Strukturen arbeiten, dann gibt es in C
eine kleine Unschönheit.

Die Deklaration
1
struct foo
2
{
3
  int bla;
4
};

deklariert foo nicht automatisch als neuen Datentyp. Daher muss
man überall wo foo verwendet werden soll immer struct foo schreiben.
Auf Dauer ist das lästig. Dafür gibt es aber den typedef.
Mittels typedef kann man für einen vorhandenen Datentyp einen
2-ten Namen festlegen
1
typedef unsigned int mUint;

würde beispielsweise vereinbaren, dass der Datentyp unsigned int
auch unter dem Namen mUint verfügbar sein soll.

Folgerichtig kann man schreiben
1
typedef struct foo mFoo;

um damit festzulegen, dass der Datentyp struct foo auch unter
dem alternativen Namen mFoo verfügbar sein soll. Damit ist
man dann die ständige Schreibere von struct im Code los.
1
struct foo myVarA;
2
mFoo myVarB;

Die beideb Variablen myVarA und myVarB sind vom exakt gleichen
Datentyp, nur ist die Schreibweise bei myVarB kürzer.
(Wir erinnern uns: mFoo ist ja nur ein anderer Name für struct foo.
mFoo ist kein eigener Datentyp, der von struct foo unabhängig wäre)

Und natürlich kann man das dann auch zusammenfassen
1
tyepdef struct foo
2
{
3
  int bla;
4
} mFoo;

definiert in einem Zug, wie eine struct foo aussieht und legt
über den typedef gleichzeitig einen zweiten Namen für struct foo
fest.

********************
1
struct light
2
{
3
  volatile uint8_t *port;
4
  volatile uint8_t *ddr;
5
  const uint8_t portpin;
6
  uint8_t pwm_value;
7
};
8
9
struct light channels[3]; 
10
11
 ...
12
 channels[0].pwm_value = 10;
13
 ...
14
15
error: expected '=', ',', ';', 'asm' or '__attribute__' before '.' token

Überprüf nochmal, ob an dieser Stelle auch tatsächlich alle Variablen
bekannt sind. Ich rate mal: Du hast den
 struct light channels[3];
in einem Header File drinnen, hast aber vergessen das Header File
zu inkludieren. Damit weiss der Compiler nicht dass channels[3]
in Wirklichkeit eine Struct Variable ist und mäkelt den . an.

von Paul H. (powl)


Lesenswert?

Ich bin jedes mal erneut erstaunt über die Qualität deiner Beiträge! ;-)
1
#include <avr/pgmspace.h>
2
3
struct light
4
{
5
  volatile uint8_t *port;
6
  volatile uint8_t *ddr;
7
  const uint8_t portpin;
8
  uint8_t pwm_value;
9
};
10
11
struct light channels[3];
12
13
channels[0].pwm_value = 10;
14
15
16
int main(void)
17
{
18
  while(1);
19
}

Habe mein Programm mal aufs Minimum reduziert aber der Fehler tritt 
trotzdem auf.


Wenn ich übrigens die pgmspace.h nicht include bekomme ich noch einen 
Fehler für die Zeile
1
volatile uint8_t *port;

expected ':', ',', ';', '}' or '__attribute__' before '*' token

von Simon K. (simon) Benutzerseite


Lesenswert?

1
channels[0].pwm_value = 10;

kannst du ja auch nicht außerhalb einer Funktion schreiben.

und um uintX_t Datentypen benutzen zu können, benötigst du den Header 
<stdint.h>

von Oliver (Gast)


Lesenswert?

1
channels[0].pwm_value = 10;

ist eine Anweisung ausserhalb einer Funktion. Das geht nunmal nicht.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:

> struct light
> {
>   volatile uint8_t *port;
>   volatile uint8_t *ddr;
>   const uint8_t portpin;
>   uint8_t pwm_value;
> };
>
> struct light channels[3];
>
> channels[0].pwm_value = 10;

Wie andere schon gesagt haben:
Das ist eine Zuweisung und Zuweisungen können nur in Funktionen
sein. Da das aber nicht in einer Funktion ist -> Fehler.

Aber ich rate mal weiter.
Du willst eine Initialisierung ! Eine Initialisierung ist
aber immer Bestandteil der Variablendefinition!
1
struct light channels[3] = 
2
  {
3
    {  PORTB, DDRB, 10 },
4
    {  PORTC, DDRC, 20 },
5
    {  PORTD, DDRD, 15 }
6
  };

Aufpassen: Auch wenn hier ebenfalls ein '=' Zeichen benutzt wird,
so ist das doch etwas fundamental anderes als das was du hast.
Du hast eine Zuweisung. Einer Variablen kann man beliebig oft
etwas zuweisen. Bei einer Zuweisung wird ein bereits vorhandener
Wert überschrieben.
In einer Variablendefinition ist das aber immer eine Initialisierung
(auch wenn die Schreibweise mittels '=' ähnlich ist). Eine
Variable kann nur einmal initialisiert werden. Bei einer
Initialisierung bekommt die Variable das erste mal einen gültigen
Wert.

(*) Letztere Unterscheidung von Überschreiben und erster Wert
ist eigentlich nur für C++ interessant. Es schadet aber nichts,
wenn man sich diese Denkweise gleich zu eigen macht.

von Johannes M. (johnny-m)


Lesenswert?

> Wenn ich übrigens die pgmspace.h nicht include bekomme ich noch einen
> Fehler für die Zeile
1
volatile uint8_t *port;
> expected ':', ',', ';', '}' or '__attribute__' before '*' token
Für uint8_t und Konsorten musst Du aber auch die inttypes.h einbinden. 
Da die pgmspace.h intern ebenfalls die Typen aus der inttypes.h braucht, 
inkludiert sie die selber, falls das noch nicht geschehen ist. Deshalb 
taucht der Fehler auch nicht auf, wenn Du pgmspace.h einbindest.

von Paul H. (powl)


Lesenswert?

1
struct light
2
{
3
  const volatile uint8_t *port;
4
  const volatile uint8_t *ddr;
5
  const uint8_t portpin;
6
  uint8_t pwm_value;
7
};
8
9
struct light channels[3] = {
10
  {&Red_PORT, &Red_DDR, Red, 0},
11
  {&Green_PORT, &Green_DDR, Green, 0},
12
  {&Blue_PORT, &Blue_DDR, Blue, 0}
13
};

Ich hoffe, ich habs nun richtig.

von Stefan E. (sternst)


Lesenswert?

1
  const volatile uint8_t *port;

Ein Zeiger auf ein uint8_t, das sowohl "const", als auch "volatile" ist?
Du wolltest wohl eher einen konstanten Pointer:
1
  volatile uint8_t* const port;

von Paul H. (powl)


Lesenswert?

Hm, ich bin mir selbst nicht darüber im klaren was ich möchte.. ich sag 
es hier mal auf deutsch, nicht auf C:

Es ist nicht geplant, dass *port, *ddr und portpin irgendwann im Laufe 
des Programms nochmal verändert werden. Die werden also am Anfang 
festgelegt. Folglich werden sie am Anfang wohl ganz normal im Speicher 
angelegt und von dort dann auch wieder gelesen.

Brauch ich dann überhaupt irgendwelche Zusätze so wie const oder 
volatile? Const funktioniert ja hier eh nicht, da die Werte sich ja 
nicht verändern und sowieso im SRAM gespeichert werden müssen um dann 
über das Strukturarray channels per index abrufbar sein zu können.

lg PoWl

von Karl H. (kbuchegg)


Lesenswert?

Paul Hamacher wrote:

> Es ist nicht geplant, dass *port, *ddr

Wie willst du dann deine Ausgaben machen, wenn sich *port
nie verändern darf. :-)

port ist ein Pointer auf den Ausgabeport.
Also ist *port dann der Wert, der am Ausgabeport ausgegeben wird.

Und der wird sich in den allermeisten Fällen während eines
Programmablaufs ändern. Sonst könnte man ja den ganzen Port
durch einen Draht zu entweder Vcc oder GND ersetzen :-)

Wenn
   ptr * ptrVariable;

ein Pointer ist (ptr sei irgendein Datentyp), dann ist
*ptrVariable, der Wert auf den der Pointer zeigt.


    ptrVariable
    +-------+              +------+
    |   o----------------->|      |
    +-------+              +------+

Gendanklich verfolgt also der * vor dem Pointernamen ganz einfach
nur den Pfeil und holt den Wert von der Speicherstelle auf die
der Pfeil zeigt (oder speichert dort einen neuen Wert im Falle
einer Zuweisung)

In deinem Fall erhebt sich die Frage: Was soll den konstant sein?
Der Pointer selbst (also das Kästchen an dem der Pfeil startet und
damit der Pfeil selbst) oder das Kästchen an dem der Pfeil endet.

Je nachdem gibt es unterschiedliche Schreibweisen:

   uint8_t * const ptrVariable;
Hier ist der Pfeil konstant. Der Pointer wird einmal initialisiert
und zeigt dann immer auf die gleiche Speicherstelle.


   uint8_t const * ptrVariable;
   const uint8_t * ptrVariable;
Hier ist das worauf der Pfeil zeigt konstant. Der Pointer (der Pfeil)
kann allerdings auf andere Kästchen zeigen, wenn ihm etwas zugewiesen
wird.

Geh einfach die Deklaration von rechts nach links durch. const
wirkt immer auf das Teil links von ihm. In

  uint8_t * const

steht unmittelbar links vom const der Pointer-Stern. Also ist auch
der Pointer konstant. Der Pointer selbst (der Pfeil) kann nicht
mehr verändert werden.

  uint8_t const *

Hier steht unmittelbar links vom const das uint8_t, also der Wert
auf den der Pointer zeigt. Also ist folgerichtig dieser Wert
konstant. Der Pointer kann sich aber ändern (weil ja rechts vom
* kein const steht)

Kleine Ausnahmeregelung: wenn links vom const nichts mehr steht,
dann wirkt das const auf das nächste Element rechts von ihm.
Daher sind

   uint8_t const  MyVar;     und
   const uint8_t  MyVar;

identisch.

Frage zum Verständnis: Was mag wohl ein

    uint8_t const * const pPtr;

aussagen?

PS: Für volatile gilt genau das gleiche wie für const.

von Paul H. (powl)


Lesenswert?

Wie immer überwältigt mich dein Post.
1
struct light
2
{
3
  volatile uint8_t * const port;
4
  volatile uint8_t * const ddr;
5
  const uint8_t portpin;
6
  uint8_t pwm_value;
7
};

Ich glaube das kommt meinem Anliegen dann schon viel näher :-)

Und hier noch die Lösung der Aufgabenstellen:
Das deklariert einen konstanten Pointer pPtr der auf einen konstanten 
8-Bit Wert zeigt :-)

Danke vielmals! Dieser Thread ist bei mir ganz oben in der 
Favoritenliste.
lg PoWl

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.