Hallo!
Habe eine Frage zur C-Programmierung ( stehe relativ am Anfang ).
Folgende struktur wird mit typedef erzeugt:
1
typedefstruct{
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!
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.
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.
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.
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.
> 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
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.
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
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.
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
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.
Rufus Τ. Firefly schrieb:> alexx schrieb:>> der ganze Ausdruck lautet>>>> (TypA*)(0x4000)-> REGB = ....>> Der Zeigeroperator -> ist die Dereferenzierung.
Eben weil x-> Äquivalent zu (*x).
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.