Forum: Mikrocontroller und Digitale Elektronik C typedef struktur definition


von alexx (Gast)


Lesenswert?

Hallo!

Habe eine Frage zur C-Programmierung ( stehe relativ am Anfang ).

Folgende struktur wird mit typedef erzeugt:
1
typedef struct {
2
3
variablen;
4
5
} TypA;

nun bin ich mit folgendem code erzeugt:
1
#define   blub   (TypA*)(0x4000)

der Define befehl ist mir bekannt. aber was hat es mit (TypA*)(0x4000) 
auf sich? wird da eine Struktur erstellt auf den nun ein Pointer zeigt? 
Aber kann ein Strukturvariablenname einen Hexwert als Namen haben?

Mir ist auch die Deklaration (TypA*) unbekannt? A la (int*)a statt int 
*a?

und warum bekommt die Struktur keinen Strukturnamen sondern nur den 
Typen?

Bin dankbar für jeden Hinweis!

von Juergen G. (jup)


Lesenswert?

(TypA*)(0x4000)

Das (TypA*) ist ein Type Cast hier ein Pointer auf einen TypA

und die (0x4000) ist die Adresse im Speicher wo blub sein soll.

Ju

von fair trades (Gast)


Lesenswert?

alexx schrieb:
> (TypA*)(0x4000)

Stichwort: cast

Die Speicheradresse 0x4000 wird als entsprechender Datentyp angesehen. 
Im Text steht dafür blub als Platzhalter.
Hier: #define als Textersetzung.

von André A. (nummer5) Benutzerseite


Lesenswert?

Anzumerken ist, dass der Compiler dadurch gezwungen wird so zu tun als 
ob an der Adresse die struct ist, es wird kein Speicherplatz reserviert. 
Ohne weitere Maßnahmen steht es dem Compiler frei diesen Speicherplatz 
zum Beispiel für globale Variablen zu nutzen. Du als Programmierer bist 
also dafür verantworlich, dem Compiler mitzuteilen, dass er den 
Speicherplatz nicht für etwas anderes nutzen soll.

von alexx (Gast)


Lesenswert?

Danke für eure Antworten!


Juergen G. schrieb:
> und die (0x4000) ist die Adresse im Speicher wo blub sein soll.

ich dachte blub ist nur ein platzhalter im code der durch den 
präprozessor durch (TypA*)(0x4000) ersetzt und dann ausgeführt wird.


fair trades schrieb:
> Die Speicheradresse 0x4000 wird als entsprechender Datentyp angesehen.

an der Adresse 0x4000 wird die Struktur im speicher abgelegt? 
interpretiere ich das richtig?



André Althaus schrieb:
> Anzumerken ist, dass der Compiler dadurch gezwungen wird so zu tun als
> ob an der Adresse die struct ist, es wird kein Speicherplatz reserviert.

warum tut der compiler so als ob? was nützt das? warum allokiert man den 
speicher nicht einfach auf normale weise mit der struktur?

bei der struktur die hier definiert wird handelt es sich um ADC-Register 
eines Mikrocontrollers, wie sie im datenblatt angegeben sind.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

alexx schrieb:
> was nützt das? warum allokiert man den
> speicher nicht einfach auf normale weise mit der struktur?
>
> bei der struktur die hier definiert wird handelt es sich um ADC-Register
> eines Mikrocontrollers, wie sie im datenblatt angegeben sind.

Genau deswegen. Die Hardware gibt die Adressen vor, an denen die 
Register zu finden sind, und deswegen kann nicht auf "normale Weise" 
Speicher allokiert werden, denn der bekommt dann irgendwelche Adressen 
im RAM zugewiesen. Um das geradezubiegen, müsste man ein wüstes 
Linkerskript mit Sektionen etc. schreiben, da ist diese "Adresse mit 
Typecast"-Variante ganz erheblich eleganter.

von Ralf (Gast)


Lesenswert?

> warum tut der compiler so als ob? was nützt das? warum allokiert man den
> speicher nicht einfach auf normale weise mit der struktur?
Siehe unten...

> bei der struktur die hier definiert wird handelt es sich um ADC-Register
> eines Mikrocontrollers, wie sie im datenblatt angegeben sind.
Aha, das erklärt's: Diese ADC-Register sind ja bereits vorhanden und 
müssen daher nicht mehr angelegt werden.

Ohne die ADC-Aussage müsstest du die Struktur tatsächlich auch noch an 
der Adresse anlegen, damit sie auch existiert. Ansonsten hättest du nur 
einen Pointer, der die entsprechende Adresse wie eine solche Struktur 
behandeln würde, aber was tatsächlich dort ist, bleibt dann dem Compiler 
überlassen...

Ralf

von alexx (Gast)


Lesenswert?

super. danke für eure hilfe!

von Edson (Gast)


Lesenswert?

Ralf schrieb:
> Ohne die ADC-Aussage müsstest du die Struktur tatsächlich auch noch an
> der Adresse anlegen, damit sie auch existiert. Ansonsten hättest du nur
> einen Pointer, der die entsprechende Adresse wie eine solche Struktur
> behandeln würde, aber was tatsächlich dort ist, bleibt dann dem Compiler
> überlassen...

Nein, das ist einzig und allein durch die Hardware bestimmt. Der 
Compiler wird den Code für Zugriffe auf diese Bereiche so übersetzen, 
wie es ihm gesagt wurde. Ein Strukturzeiger auf diesen Bereich erledigt 
das. Deine Schlussfolgerung ist also noch nicht ganz richtig.

von alexx (Gast)


Lesenswert?

an was ich mich noch störe ist das format.

mein gedanke wäre nun gewesen einen pointer zu erstellen und ihn auf die 
speicherzelle mit der adresse 0x4000 zeigen lassen.

TypA* pointer;
pointer = 0x4000;


kann man das wohl durch (TypA*)(0x4000) verkürzen? es wird also ein 
pointer erzeugt der auf die zelle mit adresse 0x4000 zeigt, dessen name 
aber unbekannt ist?

ich kenne nur das casten von variablen, also platzhaltern. 0x4000 ist 
für mich aber keine variable

von Karl H. (kbuchegg)


Lesenswert?

alexx schrieb:
> an was ich mich noch störe ist das format.
>
> mein gedanke wäre nun gewesen einen pointer zu erstellen und ihn auf die
> speicherzelle mit der adresse 0x4000 zeigen lassen.
>
> TypA* pointer;
> pointer = 0x4000;

Das geht aus einem ganz bestimmten Grund nicht.
Schau dir die Zuweisung an:
links steht der Datentyp eines Pointers
Rechts steht ein stink normaler Integer

Und die beiden sind erstmal nichts Zuweisungskompatibel.

D.h. du musst dem Compiler sagen: Das passt schon. Die 0x4000, die hätte 
ich gerne, dass du sie als Adresse auffast. Und am besten gleich als 
Adresse von so einer TypA Datenstruktur.

Genau das macht der Cast an dieser Stelle. Er 'wandelt' den Integer in 
einen Pointerdatentyp um, der den gleichen Zahlenwert hat.

Nie vergessen: Ein Zahlenwert ist nur eine Sache. Bei allem und jedem in 
C hast du immer noch die Frage des Datentyps zu klären! Bei

  float l;
  l = 5;

hast du in der Zuweisung links vom = den Datentyp float und rechts den 
Datentyp int. Die sind erst mal nicht gleich, also muss da etwas 
passieren. Die müssen aneinander angeglichen werden. In diesem Fall ist 
das einfach: der int muss zu einem float umgewandetl werden. Da das eine 
harmlose Operation ist, akzeptiert das der Compiler und fügt 
stillschweigend die entsprechende Konvertierung ein. Aber das ist nicht 
in allen Fällen so. Es gibt auch Fälle, die sind eben nicht harmlos und 
dann wird der Compiler anmerken, dass die Datentypen links und rechts 
vom = nicht zusammenpassen. Die Zuweisung eines Integers an eine 
Pointervariable ist so ein Fall. Und mit dem Cast sagt man dem Compiler: 
Passt scho!

  TypA* pointer;
  pointer = (TypA*)0x4000;


So, jetzt hast du eine Pointervariable. Wie kommst du an den Wert ran, 
wenn du eine Pointervariable hast:

  int* pI;

Du derferenzierst den Pointer.

  k = *pI;

Der vorangestellte * bedeutet Dereferenzierung. Und das bedeutet 
wiederrum: Schau unter der Adresse nach, die im Pointer gespeichert ist 
und hole den Wert von dieser Adresse.

Jetzt fügen wir mal die Teile zusammen

   TypA* pointer;
   pointer = (TypA*)0x4000;

   WertAnDieserAdresse = *pointer;

An WertAnDieserAdresse wird der Wert aus dem Speicher und zwar von der 
Speicherstelle 0x4000 und folgende (ja nachdem was genau hinter TypA 
steckt) zugewiesen.

Jetzt ändert der Pointer aber nie seinen Wert! Die Adresse ist immer 
gleich. Also braucht man doch im Grunde die ganze Variable nicht. Man 
kann also auch schreiben

  WertAnDieserAdresse = *(TypA*)0x4000;

Macht genau dasselbe (es ist einfach nur der Wert in der 
Pointervariablen an der Stelle eingesetzt an der vorher die Variable 
stand).

Und nachdem das ein bischen langweilig zu schreiben ist und Tippaufwand 
bedeutet und ausserdem schlecht zu lesen ist, 'versteckt' man einen Teil 
des Ausdrucks in einem Makro. Nimm einfach einen Teil des Textes raus, 
gib ihm einen schönen sprechenden Namen und verwende diesen Namen 
anstelle des Originaltextes.
Aus

  WertAnDieserAdresse = *(TypA*)0x4000;

wird dann zb

#define ADC_Register   (TypA*)0x4000;

  WertAnDieserAdresse = *ADC_Register;


Und jetzt schliesst sich der Kreis und wird sid wieder bei der 
Formulierung in deinem Ausgangsposting.

von Karl H. (kbuchegg)


Lesenswert?

alexx schrieb:

> ich kenne nur das casten von variablen,

Ein Cast sorgt für die Konvertierung eines Ausdrucks von einem Datentyp 
in einen anderen Datentyp.

Was ist ein Ausdruck?
Alles was einen Wert hat.
3 + 5 ist ein Ausdruck
genauso wie i + 8
oder k
oder 80
oder ....

Jedes Ding hat immer 2 Eigenschaften: Es hat einen Wert und es hat einen 
Datentyp.

   5       der Zahlenwert ist fünf
           und der Datentyp davon ist int

  int i = 8;
  i        der Zahlenwert des Ausdrucks 'i' ist 8
           und der Datentyp davon ist int

  i + 5    i ist ein int, 5 ist ein int. Also wird da eine int
           Addition gemacht, welcher wieder einen int liefert.
           Der Datentyp des Ausdrucks 'i+5' ist also int.
           Und der Zahlenwert dieses Ausdrucks ist 13

  i + 5.0  i ist ein int. aber 5.0 ist ein double.
           Das passt erst mal nicht. Unterschiedliche Datentypen
           können nicht miteinander verrechnet werden. Also muss
           etwas geschehen.
           In diesem Fall wird der int (also das i) implizit zu
           einem double umgecastet. D.h. aus dem int wird zuerst
           ein double gemacht. Damit wird hier double mit double
           addiert und das Ergebnis ist wieder double.
           Der Datentyp des Ausdrucks 'i+5.0' ist also double
           Und der Zahlenwert davon ist 13.0

von alexx (Gast)


Lesenswert?

Hallo Hr. Buchegger,

Vielen Dank für ihre Ausführungen. Das hat mir sehr geholfen.

Wo sich der Kreis allerdings nicht ganz schließt, ist die Geschichte mit 
der Dereferenzierung:

Ich finde im Code nicht

*(TypA*)(0x4000)

sondern nur

(TypA*)(0x4000).

der ganze Ausdruck lautet

(TypA*)(0x4000)-> REGB = ....

es soll also auf die Struktur die in 0x40000 liegt zugegriffen werden 
und deren Register B beschrieben werden. mit dem * als dereferenzierung 
vorne dran, würde das  ganze noch einigermaßen sinn machen für mich.

von Stefan E. (sternst)


Lesenswert?

1
(TypA*)(0x4000)->REGB
ist das Gleiche wie
1
(*(TypA*)(0x4000)).REGB

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

alexx schrieb:
> der ganze Ausdruck lautet
>
> (TypA*)(0x4000)-> REGB = ....

Der Zeigeroperator -> ist die Dereferenzierung.

von Cyblord -. (cyblord)


Lesenswert?

Rufus Τ. Firefly schrieb:
> alexx schrieb:
>> der ganze Ausdruck lautet
>>
>> (TypA*)(0x4000)-> REGB = ....
>
> Der Zeigeroperator -> ist die Dereferenzierung.

Eben weil x-> Äquivalent zu (*x).

von Hmm (Gast)


Lesenswert?

Ich würde sogar behaupten wollen, das man mit dem Ausdruck
x->y

die Strukturmitglied zuweist (wenn rechts) oder ausliest (wenn links) 
auf das die Adresse in x verweist.

Um ein übriges zu tun würde ich weiter behaupten,
das dies äquivalent zu

z.y

falls z = *y.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Gewiss.

(Machen wir hier den Schulze&Schultze-Wettbewerb?)

von Hmm (Gast)


Lesenswert?

>(Machen wir hier den Schulze&Schultze-Wettbewerb?)

Das habe ich mich bei Deiner Antwort 
(Beitrag "Re: C typedef struktur definition") auch gefragt. :-)

von alexx (Gast)


Lesenswert?

Mein Rätsel ist gelöst. Danke an alle!

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.