Forum: Mikrocontroller und Digitale Elektronik Was macht diese C Code-Zeile ?


von Alex (Gast)


Lesenswert?

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

von Karl M. (Gast)


Lesenswert?

Guten Morgen,

Define ist hier ein Text Ersatz,

Das heißt was links steht GPIOA wird mit dem Text rechts ersetzt.

von Karl (Gast)


Lesenswert?

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

von Alex (Gast)


Lesenswert?

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 ?

von N. M. (mani)


Lesenswert?

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.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

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.

von fop (Gast)


Lesenswert?

Das bedeutet, wann immer Du
1
GPIOA
schreibst, bekommt der Compiler
1
((GPIO_TypeDef *) ((((uint32_t)0x40000000) + 0x10000) + 0x0800))
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.

von Alex (Gast)


Lesenswert?

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 ?

von M.K. B. (mkbit)


Lesenswert?

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.

von Bernd K. (prof7bit)


Lesenswert?

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.

: Bearbeitet durch User
von Random .. (thorstendb) Benutzerseite


Lesenswert?

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 :-)

: Bearbeitet durch User
von N. M. (mani)


Lesenswert?

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.

von fop (Gast)


Lesenswert?

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.

von fop (Gast)


Lesenswert?

1
(uint32_t)0x40000000
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.

von Alexander S. (proxxy)


Lesenswert?

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

: Bearbeitet durch User
von Alex (Gast)


Lesenswert?

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

von Alex (Gast)


Lesenswert?

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 ?

von Markus F. (mfro)


Lesenswert?

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).

von Alex (Gast)


Lesenswert?

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 :)

von Brummbär (Gast)


Lesenswert?

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:
1
GPIOA = 15; // Da ist Dein "="
Der Präprozessor macht daraus
1
((GPIO_TypeDef *) 0x40010800) = 15;

von Alex (Gast)


Lesenswert?

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)

von Alex (Gast)


Lesenswert?

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" ?

von S. R. (svenska)


Lesenswert?

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).

von Brummbär (Gast)


Lesenswert?

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.

von Alex (Gast)


Lesenswert?

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

von Ohu (Gast)


Lesenswert?

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

von Brummbär (Gast)


Lesenswert?

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.

von Jan (Gast)


Lesenswert?

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?

von Alex (Gast)


Lesenswert?

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

von Alex (Gast)


Lesenswert?

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 ?

von Brummbär (Gast)


Lesenswert?

Alex schrieb:
>     *(&a) = 10;

Du hast einen dereferenzierten Zeiger auf "a". Also wieder die Variable 
selber.

von Alex (Gast)


Lesenswert?

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 ?

von Brummbär (Gast)


Lesenswert?

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.

von Alex (Gast)


Lesenswert?

Brummbär schrieb:
> Der Compiler weiß, dass der Datentyp von a ein int ist

das habe ich befürchtet :D

von Markus F. (mfro)


Lesenswert?

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:
1
float a = 20.0;
2
int ad = (int) &a;  // die Adresse von a als int
3
4
* (float *) ad = 22.0;

von Alex (Gast)


Lesenswert?

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

von Alex (Gast)


Lesenswert?

ich meinte natürlich

float a = 22.0;

von S. R. (svenska)


Lesenswert?

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.

von Dumdi D. (dumdidum)


Lesenswert?

Das a->b nur eine Abkürzung für (*a).b ist, ist Dir bekannt, oder liegt 
da das Problem?

von Alex (Gast)


Lesenswert?

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.)

von Alex (Gast)


Lesenswert?

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.

von Alex (Gast)


Lesenswert?

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

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.