Hallo,
folgende Code-Zeile aus der stm32f10x.h versuche ich nun schon seit
längerem zu verstehen, komme aber einfach nicht dahinter was hier
passiert:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
vllt kann mir jemand Schritt für Schritt erklären, was hier genau
passiert bzw. gemacht wird. Ich möchte mir die uC Programmierung selber
beibringen und komme auch jeden Tag einen Schritt weiter, jedoch gibt es
immer wieder Hürden, die mir das Leben schwer machen :(
Vielen Dank für Eure Hilfe
Gruss
Alex
Gpio Base ist eine Zahl die die Adresse der Peripherie enthält. Die wird
in einen Zeiger auf die Peripherie gecastet.
Das define ersetzt gpioa durch den Teil rechts, so dass man es als
Verwender schöner schreiben kann
Karl schrieb:> Gpio Base ist eine Zahl die die Adresse der Peripherie enthält. Die wird> in einen Zeiger auf die Peripherie gecastet.
Guten Morgen und vielen Dank euch Beiden.
genau, das #define ersetzt den text durch den Präprozessor. Was links
steht, wird durch das was rechts steht, ersetzt.
Das eigentliche Kopfzerbrechen macht mir dieser "Cast". GPIO_Base
Wenn ich in dem Headerfile die Spur von GPIO_Base zurückverfolge,finde
ich, dass das eine Integerzahl ist:
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
#define PERIPH_BASE ((uint32_t)0x40000000)
was genau macht dieser Cast am Ende ?
((GPIO_TypeDef *) GPIOA_BASE)
GPIO_TypeDef ist ja eine Benutzerdef.-Datentyp, eine Struct:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
__IO uint32_t BSRR;
__IO uint32_t BRR;
__IO uint32_t LCKR;
} GPIO_TypeDef;
Wie wird hier diese GPIOA_BASE mit dem (GPIO_TypeDef *) umgewandelt ?
Was kann ich nach diesem Cast mit GPIOA_BASE machen ?
Die Adresse GPIOA_BASE wird auf einen Pointer vom Typ GPIO_TypeDef
gecastet.
Der Preprozessor ersetzt GPIOA_BASE also durch ((GPIO_TypeDef *)
GPIOA_BASE). Damit kannst du einfach über GPIOA_BASE->x auf die
Registerinhalte von GPIOA zugreifen. Wenn es das nicht gäbe müsstest du
jedesmal ((GPIO_TypeDef *) GPIOA_BASE)->x schreiben.
Alex schrieb:> Wie wird hier diese GPIOA_BASE mit dem (GPIO_TypeDef *) umgewandelt ?
Die Peripherie ist in den Speicher gemappt an einer bestimmten Adresse.
Startend ab dieser Adresse gibt es Register, die in dem Struct definiert
sind, um sie zu steuern.
Jetzt möchte man quasi das Struct mit der Adresse startend bei der
Adresse der Peripherie haben, damit man leichter auf die Register
zugreifen kann.
Daher castet man einen Pointer auf die Basis-Adresse der Peripherie zu
einem Struct-Pointer und voila.
Jeder Zugriff auf das Struct landet dann bei der richtigen Peripherie.
vorgesetzt. Das bekommt man raus, wenn man alle Textersetzungen durch
die Defines ineinander einsetzt. Viele Compiler kennen auch eine
Kommandozeilenoption, um das Ergebnis der Präcompilerbemühungen als
Datei abzuspeichern.
Wenn man jetzt mal selber diese Konstanten aufaddiert, bleibt von diesem
Ausdruck
1
((GPIO_TypeDef*)0x40010800)
übrig.
Also ein Zeiger auf eine Struktur vom Typ GPIO_TypeDef, die auf Adresse
0x40010800 zeigt.
Verwendet wird das Ding in dem man
1
GPIOA->ODR=42UL;
schreibt. Das Dereferenziert den Zeiger und wählt das Element ODR der
Struktur.
N. M. schrieb:> Der Preprozessor ersetzt GPIOA_BASE also durch ((GPIO_TypeDef *)> GPIOA_BASE)
Die ursprüngliche Zeile war ja:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
Ersetzt wird doch das GPIOA mit ((GPIO_TypeDef *) GPIOA_BASE) vom
Preprozessor? Oder habe ich dich jetzt falsch verstanden?
Wenn man ganz einfach nur die Datentypen betrachtet sieht der Cast
rechts doch folgendermaßen aus:
(GPIO_TypeDef *) uint32_t
Diese Typenumwandlung verstehe ich nicht ganz. Eine Integerzahl, welche
eine Adresse darstellen soll, wird in einen Zeiger umgewandelt ?
Alex schrieb:> Diese Typenumwandlung verstehe ich nicht ganz. Eine Integerzahl, welche> eine Adresse darstellen soll, wird in einen Zeiger umgewandelt ?
fop hat ja schon ausgerechnet was der Wert der Zahl ist. Den wirst du so
auch im Datenblatt des Prozessors als Adresse für die GPIOA finden.
Normalerweise kann man Zahlen nicht einfach als Pointer verwenden, außer
man weiß was man tut und das ist hier auch der Fall.
Mit dem Cast sagst du dem Compiler folgendes:
Hier ist eine Zahl und ich bin mir 100% sicher, dass diese Zahl eine
Adresse ist, an der eine Struktur des Typs GPIO_TypeDef liegt (In diesem
Fall Register, die auf diese Adresse gemapped sind und kein RAM.). Im
weiteren kannst du dann über den Pointer auf die Struktur zugreifen und
musst die einzelnen Adressoffsets der Register nicht einzeln ausrechnen.
Alex schrieb:> Eine Integerzahl, welche> eine Adresse darstellen soll, wird in einen Zeiger umgewandelt ?
Ja. Zum Beispiel
(void*) 42
würde einen typlosen Zeiger darstellen der auf die Adresse 42 zeigt (bzw
auf die Adresse die sich ergibt wenn man die Zahl 42 als Adresse
interpretieren würde was ja ziemlich leicht ist bei einer Architektur
bei der der ganze Speicher linear adressiert wird und man jede Adresse
auch als Zahl eindeutig ausdrücken kann).
Das macht man aber natürlich tunlichst nur bei Adressen die hart
verdrahtet oder aus anderen Gründen immer fest sind, alle anderen Zeiger
(solche auf Variablen zum Beispiel) sollte man dem Compiler überlassen
denn der kann die hinlegen wo er es für richtig erachtet.
Alex schrieb:> (GPIO_TypeDef *) uint32_t>> Diese Typenumwandlung verstehe ich nicht ganz. Eine Integerzahl, welche> eine Adresse darstellen soll, wird in einen Zeiger umgewandelt ?
Die struct bildet die Struktur des Peripherals ab. Bedeutet: Die Offsets
der member der Struktur entsprechen den gleichnahmigen Registern des
jeweiligen Peripherals.
Mit dem "Zusammenführen" wird dem Compiler jetzt gesagt, dass an der
Adresse x ein Stück Speicher liegt, vom Typ der Struktur. Greifst du
jetzt auf ein Member der Struktur zu, so rechnet der Compiler die
Startadresse plus den Offset innerhalb der Struktur als Adresse für den
Zugriff.
Schaut nach CMSIS aus. Solche Headerfiles aus SVD zu generieren ist
spassig. Geht wunderbar in C/C++, wenn man sich Tabellen für
verschachtelte struct/union Konstrukte aufbauen muss, aus welchem die
Peripherals manchmal bestehen. Z.B. CRC Peris etc. Siehe SVDConv.exe :-)
Alex schrieb:> Die ursprüngliche Zeile war ja:
Ja und ich habe beschrieben was man damit machen kann.
Alex schrieb:> Ersetzt wird doch das GPIOA mit ((GPIO_TypeDef *) GPIOA_BASE) vom> Preprozessor? Oder habe ich dich jetzt falsch verstanden?
Genau
Alex schrieb:> Diese Typenumwandlung verstehe ich nicht ganz. Eine Integerzahl, welche> eine Adresse darstellen soll, wird in einen Zeiger umgewandelt ?
Genau. Der Hintergrund ist, das man den Komfort haben möchte über eine
Struktur auf die einzelnen Registerinhalte zugreifen zu können.
Man definiert also die Struktur GPIO_TypeDef um den Aufbau der Register
zu beschreiben.
Damit ist aber noch nicht beschrieben wo diese Struktur zum Einsatz
kommt (Adresse).
Also wird eine Adresse (int) auf einen Pointer vom Typ GPIO_TypeDef
gecastet.
Erst jetzt weiß der Compiler das er auf die angegebene Adresse über die
Strukturfelder zugreifen kann.
Die Antwort auf die Frage : "Kann ich aus einer Integerzahl einen
Pointer machen ?" ist gar nicht so sicher, wie viele denken.
Normalerweise wird in eigentlich jeder Prozessorarchitektur der Speicher
durchnummeriert. Hätte der Ikea - Gründer statt Möbel Mikrcontroller
gebaut, hätte er stattdessen den Speicherzellen vielleicht auch
schwedische Begriffe als Namen, um sie anzusprechen, gegeben. Dann würde
ein Programmierer, der einen Compiler für das Ding baut, einen Zeiger
auf Speicherstellen ganz anders umsetzen. Wie ein Compiler Zeiger
umsetzt ist dessen Erstellern überlassen. In Systemen, in denen man
Speicherstellen über eine Zahl auswählt, wird mit sehr hoher
Wahrscheinlichkeit also auch eine Zahl genutzt, um den Wert eines
Pointers darzustellen. Damit ist es dann möglich zu schreiben, tu mal so
als ob das Ergebnis von 6*9 ein Zeiger wäre. Und es führt eigentlich
immer zu dem Ergebnis, dass beim Dereferenzieren dieses Zeigers die
eigentlichen Daten auf Adresse 54 gesucht werden.
Praktisch, wenn Dateien mit solchen Tricks zusammen mit dem Compiler
geliefert wurden. Dann kann man sich doch sicher sein, dass die Tricks
mit dem Compiler so funktionieren, wie geplant.
ist übrigens auch so ein Typecast. Betrachte die Zahl 0x40000000 als 32
Bit lange Zahl ohne Vorzeichen. 0x40000000 alleine ist eine Zahl mit
Vorzeichen, in der Lieblingslänge der Ersteller des Compilers.
Könnte man dann auch anstelle von:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
schreiben:
GPIO_TypeDef* GPIOA = ((GPIO_TypeDef *) GPIOA_BASE);
?
also rein von der Richtigkeit der Syntax
Wahnsinn Was für ein geniales Forum! Soviele wertvolle Antworten, welche
man so in keinem Buch findet. Ich wünschte ich wäre schon früher auf
euch gekommen :)
Diese #define Sachen werden also nur textmässig ersetzt und dienen nur
der Übersichtlichkeit und Einfachheit für den Programmierer.
Dieser Cast wird erst dann wirklich durchgeführt, wenn er an der Stelle
im Code vom Compiler eingesetzt wird und nicht schon in der #define
Anweisung selbst. Hier schaut der Preprozessor, wo ist der linke text
und wird durch den rechten text ausgetauscht.
Vielen Dank erstmal Euch allen
fop schrieb:> Wenn man jetzt mal selber diese Konstanten aufaddiert, bleibt von diesem> Ausdruck((GPIO_TypeDef *) 0x40010800)übrig.> Also ein Zeiger auf eine Struktur vom Typ GPIO_TypeDef, die auf Adresse> 0x40010800 zeigt.> Verwendet wird das Ding in dem manGPIOA->ODR=42UL;schreibt. Das> Dereferenziert den Zeiger und wählt das Element ODR der> Struktur.
Ich habe noch ein Verständnisproblem mit dem Casten:
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
von C her kenne ich das Casten eines Pointers z.B.. wenn ich einen Char
Pointer auf INT caste, dann sieht das so aus:
int* ptr;
char* cPtr = "S";
ptr = (int*) cPtr;
dann weiss ich, dass ptr ein int-zeiger auf eine ursprüngliche
Char-Adresse ist.
anders wäre doch wenn ich nur:
(int*) cPtr;
schreiben würde, wie es ja grundsätzlich bei
((GPIO_TypeDef *) GPIOA_BASE)
der Fall ist.
ich vermisse da ein "=" also eine Zuweisung damit ich mit diesem
gecasteten Pointer etwas anfangen kann. Ein Cast erzeugt doch kein Objet
der Struktur GPIO_TypeDef oder denke ich hier viel zu kompliziert ?
Alex schrieb:> int* ptr;> char* cPtr = "S";> ptr = (int*) cPtr;>> dann weiss ich, dass ptr ein int-zeiger auf eine ursprüngliche> Char-Adresse ist.
Streng genommen weißt Du das nicht.
Tatsächlich ist es sogar nicht unwahrscheinlich, daß das undefined
behaviour ist (nämlich dann, wenn das alignment nicht paßt).
Wenn Du sicherstellen willst, daß der pointer cast kein UB ist, mußt Du
erst sicherstellen, daß der char-Pointer das korrekte Alignment hat
(sicher ist das beispielsweise, wenn der Zeiger ursprünglich als cast
aus einem int-pointer entstanden ist oder der char Teil einer union ist,
die int-alignment erzwingt).
darum ging es mir jetzt nicht, aber ich weiss schon was du meinst: wenn
ich mit einem pointer-cast plötzlich undefinierten inhalt bekomme kann
schnell was schief gehen
mein Problem war das denken in Adressen. Die C Programmierung für einen
Microkontroller ist ein ganzes Stück anders, als das "gewöhnliche" C was
ich noch gelernt hab.
Schlimm finde ich dann noch, wenn man die Headerdateien von so einem
STM32F1xx anschaut, wie verschachtelt alles ist. Tausende #defines
preprozessor anweisungen wo man Schritt für Schritt zurückgehen muss,
bis man dann endlich am Ursprung ist. Oder man froh ist wenn man mal ein
bekanntes Wort wie "struct" findet und sonst nur benutzerdefinierte
typen-namen typedefs und defines..
Klar, so funktioniert gut strukturierte Programmierung, aber ich tue
mich da noch ziemlich schwer, zumal ich mir das alles selber beibringen
möchte . ich pauke jeden Tag :)
Alex schrieb:> ich vermisse da ein "=" also eine Zuweisung damit ich mit diesem> gecasteten Pointer etwas anfangen kann. Ein Cast erzeugt doch kein Objet> der Struktur GPIO_TypeDef oder denke ich hier viel zu kompliziert ?
Das "=" wird dann auch irgendwo im Programm im Zudammenhang mit dem
definiertem Makro verwendet.
Irgendwo wird dann stehen:
Brummbär schrieb:> Irgendwo wird dann stehen:GPIOA = 15; // Da ist Dein "="> Der Präprozessor macht daraus((GPIO_TypeDef *) 0x40010800) = 15;
Danke Brummbaer für Deine Anwort !
hier ist es aber nicht so:
GPIOA->CRL = (1 << 21);
denn das ist genau das:
((GPIO_TypeDef *) ((((uint32_t)0x40000000) + 0x10000) + 0x0800))->CRL =
(1 << 21);
der Cast an sich wirkt wie ein Zeiger auf ein instanziertes Objekt vom
Typ GPIO_TypeDef bei der Aresse 0x40010800
Das ist der Knackpunkt weil ich das von C her so nicht kenne. Ein Cast
ist/war für mich bisher eine Typenumwandlung, aber kein Erzeugen einer
Variable oder eines Zeigers, denn mit:
(int*) cPtr;
passiert doch erstmal auch nichts weil ich das nichts zuweise wie z.b.
mit:
int ptr = (int*) cPtr;
(als Beispiel)
was ich genau meine ist, dass ich mit
GPIOA->CRL
gleich auf den Member CRL eines plötzlich bestehenden Objektes zugreifen
kann.
GPIOA ist doch erstmal nur ein cast, und keine Zuweisung einer Adresse
an eine Pointervariable oder habe ich hier einen "Knick in der Optik" ?
Alex schrieb:> Das ist der Knackpunkt weil ich das von C her so nicht kenne. Ein Cast> ist/war für mich bisher eine Typenumwandlung, aber kein Erzeugen einer> Variable oder eines Zeigers,
Genau das passiert doch:
Der Wert 0x40010800 (vom Typ "int") wird genommen und dessen Typ wird
auf "GPIO_TypeDef *" geändert. Mehr passiert nicht.
Was sich ändert, ist die Interpretation von "0x40010800 ist eine Zahl"
zu "0x40010800 ist die Adresse von einem GPIO_TypeDef".
Eine Variable ist ein Stück Speicher mit einem Wert drin.
Eine Zeigervariable ist ein Stück Speicher mit einem Zeiger drin.
Daraus folgt: Eine Zeigervariable ist eine besondere Variable, und ein
Zeiger ist ein besonderer Wert. Also bitte verwechsle "Zeiger" (eine
Zahl) nicht mit "Zeigervariable" (ein Stück Speicher).
Alex schrieb:> Ein Cast> ist/war für mich bisher eine Typenumwandlung
Und genau das ist es nicht. Es wird hier nichts umgewandelt, sondern nur
anders interpretiert. Normalerqweise verhindert die Typenprüfung, dass
Du einem Zeiger kein Integer zuweisen kannst. Diese Prüfung wird mit
einem Cast außer Kraft gesetzt.
S. R. schrieb:> Der Wert 0x40010800 (vom Typ "int") wird genommen und dessen Typ wird> auf "GPIO_TypeDef *" geändert. Mehr passiert nicht.>> Was sich ändert, ist die Interpretation von "0x40010800 ist eine Zahl"> zu "0x40010800 ist die Adresse von einem GPIO_TypeDef".
Ok dann ist das ein Pseudo-Zeiger ? also nicht wie im klassichen C:
int a;
int*ptr = &a;
ptr ist ein int zeiger auf die adresse von a
während:
((GPIO_TypeDef *) 0x40010800)
ein Cast ist: INT zahl wird von nun an als Adresse interpretiert. Als
Adresse darum, weil ich ein "*" Sternchen angebe.
Wenn etwas als Adresse interpretiert werden soll, ist das für den
Compiler automatisch ein Zeiger
auch wenn ich hier keinen Zeigernamen habe sondern "nur" eine Adresse
worauf gezeigt wird, muss ich auch den gesamten Cast-Ausdruck dem
Compiler immer wieder vor die Nase legen richtig ?
Ich glaube die Antwort auf mein Verständisproblem hätte ich wenn ich
wüsste wie ein Compiler denkt, dies interpretiert
Alex schrieb:> muss ich auch den gesamten Cast-Ausdruck dem> Compiler immer wieder vor die Nase legen richtig ?
Der Compiler will eben wissen welchen Typ er verabeiten soll (ein
Kernprinzip von C ist es ja dass die Typen festgelegt sind)
Wenn du eine Nummer hinschmeißt musst du halt dazu sagen was es ist.
Im Laden sagst du halt ich will 5 (Äpfel) und 5 (Eier) und 0x20 (kilo
Hackfleisch) - ist das gleiche Prinzip
Alex schrieb:> Ok dann ist das ein Pseudo-Zeiger ? also nicht wie im klassichen C:
Das ist klassisches C.
Ein Zeiger ist einfach eine Zahl die zudem den Typ "Zeiger" hat.
Wenn Du eine Zahl vom Typ "Integer" hast und diese einem Zeiger zuweisen
willst, wirst Du mit einem Fehler darauf hingewiesen. Wenn Du aber
100%ig sicher bist, das das trotzdem gemacht werden soll, schreibst Du
einen Cast vorweg.
Die Zahl bleibt immer noch eine Zahl.
Der gecastete Integer ist dann ein echter Zeiger und nicht nur ein
merkwürdiges Konstrukt.
Das ist kein "Pseudozeiger". Es ist ein Link zu einem Stück Speicher mit
einer Adresse.
Bei deinem Beispiel berechnest du die Adresse vom int a über den &
Operator und weist ihn einer Variablen ptr, zu. ptr ist ebenfalls ein
Stück Speicher, der nur jetzt eben mit der Adresse von a belegt ist.
Eine Abkürzung sozusagen, du könntest auch *(&a) = 4; schreiben.
Jetzt kommt aber der Trick: Anstatt, dass du die Adresse von a mit &
herausbekommen musst, ist die GPIOA_BASE Adresse vorher bekannt.
Definiert durch ST Micro. Also brauchst du die Variable ptr nicht - es
ist ein Platzhalter und eine nette Möglichkeit vom Compiler, die Adresse
von a automatisch ermitteln zu können. Brauchst du nicht, du willst ja
die Speicherstelle direkt ansprechen, weil du nämlich weist, dass dort
die Register deines GPIOA liegen und zwar genau in der Form, wie sie
dein GPIO_TypeDef struct zeigt. Das ist aber per Design so, nicht
einfach so.
Natürlich kannst du auch sowas schreiben:
GPIO_TypeDef * myPtr = GPIOA;
und dann über myPtr->RegA ansprechen. Aber möchtest du jedes Mal eine
neue Variable im Ram erstellen, um dein GPIO anzusprechen?
Vielen Dank für Eure Antworten. Ihr erklärt das wie aus Sicht des
Compilers. Ich dachte ich hätte Zeiger und deren Syntax schon
verstanden, aber der Cast hat mir eigentlich das Gegenteil gezeigt...
aber vieles was ich jetzt von euch erklärt bekommen habe findet man in
keinem Lernbuch so erklärt.
Ich denke der "Groschen" ist jetzt auch bei mir gefallen. Ich werde
jetzt erstmal mit dem hier weiter üben gehen
Es hat funktioniert !! Ohne dass ich eine Pointer Variable gebraucht
habe:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a = 20;
*(&a) = 10;
printf("Variable A hat jetzt den Wert:%d\n",a);
return 0;
}
ich wollte hiermit ausprobieren, was mein Vorredner Jan erklärt hat mit
der kürzeren Schreibweise. Jetzt habe ich aber einen Typenlosen Zeiger
der auf die Adresse von a zeigt ?
Brummbär schrieb:> Du hast einen dereferenzierten Zeiger auf "a". Also wieder die Variable> selber.
ok, aber muss ich beim Zeigen nicht wissen auf welchen Datentyp ich
zeige ?
Der Compiler weiß, dass der Datentyp von a ein int ist.
Daher weiß er auch dass &a ein Zeiger auf eine Int-Variable und *(&a)
wieder die Int-Variable selber ist.
Alex schrieb:> Brummbär schrieb:>> Du hast einen dereferenzierten Zeiger auf "a". Also wieder die Variable>> selber.>> ok, aber muss ich beim Zeigen nicht wissen auf welchen Datentyp ich> zeige ?
Wenn Du das "nachbauen" willst, mach' so:
Markus F. schrieb:> Wenn Du das "nachbauen" willst, mach' so:> float a = 20.0;> int ad = (int) &a; // die Adresse von a als int>> * (float *) ad = 22.0;
ok
1. a bekommt wert 20.0 vom typ float
2. int ad speichert adresse von a als int-zahl (gecastet)
3. ad wird floatzeiger und zeigt wieder auf a und überschreibt den
inhalt mit 22.0
int a ist nun 22.0 !!
also bei der letzten Zeile kam ich ganz schön ins straucheln und musste
bestimmt 5 minuten überlegen ..
vllt habe ich für heute auch zuviel gemacht. ich werde morgen die Sache
nochmals angehen.
Gute Nacht
Alex schrieb:>> Der Wert 0x40010800 (vom Typ "int") wird genommen und dessen Typ wird>> auf "GPIO_TypeDef *" geändert. Mehr passiert nicht.>>>> Was sich ändert, ist die Interpretation von "0x40010800 ist eine Zahl">> zu "0x40010800 ist die Adresse von einem GPIO_TypeDef".>> Ok dann ist das ein Pseudo-Zeiger ?
Nein, das ist ein Zeiger wie jeder andere auch.
> also nicht wie im klassichen C:>> int a;> int*ptr = &a;>> ptr ist ein int zeiger auf die adresse von a
Das steht oft so geschrieben, aber ptr ist eine Zeigervariable, die
einen Zeiger auf die Adresse von a enthält.
Ein Zeiger ist eine Zahl.
Eine Zeigervariable ist eine Variable.
Der Unterschied muss dir im Kopf klar sein, es wird aber nur selten so
klar unterschieden (auch in Lehrbüchern nicht).
> während:> ((GPIO_TypeDef *) 0x40010800)> ein Cast ist: INT zahl wird von nun an als Adresse interpretiert.
Es gibt kein "von nun an". Du hast den Compiler gezwungen, diese eine
Zahl in diesem einen Ausdruck für dieses eine Mal anders zu
interpretieren, als er es sonst gemacht hätte. Genau das ist ein Cast.
> Als Adresse darum, weil ich ein "*" Sternchen angebe.> Wenn etwas als Adresse interpretiert werden soll, ist das für den> Compiler automatisch ein Zeiger
Genau. In C werden Zeigertypen mit Sternchen markiert.
Zeiger und Adressen sind das gleiche (die historischen Sonderfälle, wo
das nicht gilt, ignoriere ich jetzt mal).
> auch wenn ich hier keinen Zeigernamen habe sondern "nur" eine Adresse> worauf gezeigt wird, muss ich auch den gesamten Cast-Ausdruck dem> Compiler immer wieder vor die Nase legen richtig ?
Richtig, weil du jedesmal, wenn du eine vom Normalfall abweichende
Interpretation haben willst, den Compiler erneut dazu zwingen musst.
S. R. schrieb:> Der Unterschied muss dir im Kopf klar sein, es wird aber nur selten so> klar unterschieden (auch in Lehrbüchern nicht).
Vielen Dank Svenska, du hast damit tatsächlich eine Schwachstelle bei
mir gefunden. Meine Denkweise über Zeiger war hier immer völlig falsch.
Der Cast muss dem Compiler jedesmal aufgezwungen werden
Und ganz wichtig :
Der Zeiger ist eine Zahl und keine Zeigervariable. Eine Zeigervariable
enthält nur diese Zahl die ein Zeiger ist. Eine Zeigervariable ist aber
nicht immer nötig (wie bei diesem Cast z.B.)
Dumdi D. schrieb:> Das a->b nur eine Abkürzung für (*a).b ist, ist Dir bekannt, oder liegt> da das Problem?
Hallo Dumdi,
ja das war mir zum Glück schon bewusst.
Ein vollwertiger Zeiger auf die Adresse 0x40010800 ist:
((GPIO_TypeDef*) 0x40010800)
0x40010800 nach dem cast ist wieder eine ganz normale zahl
Deswegen habe ich immer verzweifelt nach einer Pointer-Variable gesucht
und
nie verstanden