Forum: Compiler & IDEs Frage zu Zeiger: Cast?


von schlau-werden-Wollender (Gast)


Lesenswert?

Diesen Schnipsel hab ich hier (Programmbeispiel DMA für Raspberry PI):
1
int main() {
2
    volatile uint32_t *gpioBaseMem, *dmaBaseMem, *pwmBaseMem, *timerBaseMem, *clockBaseMem;
3
4
         .
5
         .
6
         .
7
8
    int outPin = 10;
9
    //now set our pin as an output:
10
    volatile uint32_t *fselAddr = (volatile uint32_t*)(gpioBaseMem + GPFSEL0/4 + outPin/10);
11
    writeBitmasked(fselAddr, 0x7 << (3*(outPin%10)), 0x1 << (3*(outPin%10)));

Was hat es mit der vorletzten Zeile auf sich? Für mich sieht das aus als 
ob der Zeiger *gpioBaseMem auf *gpioBaseMem gecasted wird - was ich für 
wirres Zeug halten würde. Oder hat das seinen Grund?

von ccccccc (Gast)


Lesenswert?

naja outPin ist vom Datentyp int. GPSEL0 wird nicht klar was das für ein 
Datentyp ist. Da es in der 2ten Klammer der vorletzten Zeile zu einer 
implizieten Typumwandlung kommt, wandelt man mit (volatitle uint32_t*) 
wieder zurück zum richtigen Typ. Kann man durchaus so machen.

von MWS (Gast)


Lesenswert?

schlau-werden-Wollender schrieb:
> Für mich sieht das aus als
> ob der Zeiger *gpioBaseMem auf *gpioBaseMem gecasted wird - was ich für
> wirres Zeug halten würde. Oder hat das seinen Grund?

Ich hätte jetzt gesagt, dass die geklammerten Berechnungen für den 
Zeiger mit einem Defaulttyp ausgeführt werden. Da dieser sich von der 
Zeigerbreite unterscheiden kann, teilt man dem Compiler per Cast mit, 
welche Bitbreite er für temporäre Ergebnisse verwenden soll.

ccccccc schrieb:
> naja outPin ist vom Datentyp int. GPSEL0 wird nicht klar was das für ein
> Datentyp ist.

schlau-werden-Wollender schrieb:
> mit der vorletzten Zeile

von Rainer B. (katastrophenheinz)


Lesenswert?

sieht für mich aus wie ganz normale Zeigerarithmetik:
fSelAddr = gPioBaseMem + irgendwie berechneter Offset
Der Typecast davor dient dazu, auch das Ergebnis 'volatile' zu machen.

von schlau-werden-Wollender (Gast)


Lesenswert?

Autor: ccccccc (Gast) schrieb:
>GPSEL0 wird nicht klar was das für ein
>Datentyp ist.

Das gehört noch dazu:
1
#define GPFSEL0   0x00000000 //gpio function select. There are 6 of these (32 bit registers)

Sehe ich das richtig: Du meinst, daß:
int outPin = 10;
den Zeiger hier;
uint32_t *gpioBaseMem
auf ein int castet? Oder es zumindest zu einer Warnung/Fehlermeldung 
kommt?

von ccccccc (Gast)


Lesenswert?

Der Cast muss ja nicht unbedingt gemacht werden. Sprich
1
(gpioBaseMem + GPFSEL0/4 + outPin/10)
kann auch den richtigen Datentyp zurückgeben, das weiß ich jetzt nicht 
auswendig. Allerdings ist es schöner das ganze nochmal explizit 
darzustellen, dass das Ergebniss der Berechnung nochmal mit
1
(volatile uint32_t*)
zu casten.
So seh ich das zumindest, jetzt kommt eh gleich wieder: Ja warum 
angeben, das wird alles unleserlich. Ich finde man kann solche casts 
durchaus nochmal machen. Nach dem Python-Grundsatz: "Explicit is better 
than implicit"

von Karl H. (kbuchegg)


Lesenswert?

Die wahrscheinlichste Erklärung dürfte sein, dass der Programmierer am 
Anfang gpioBaseMem noch nicht als Pointer zu volatile hatte und dann 
einfach überall ein volatile mit der Gieskanne ergänzt hat. Vielleicht 
hatte er auch irgendwelchen Ärger und dann einfach mal rumgecastet. Das 
kommt leider häufiger vor, als man denkt, dass wenn etwas nicht so 
funktioniert wie man sich das vorstellt, dass dann einfach mal wild 
rumgecastet wird.
Rein formal ist der Cast hier nicht notwendig.

von Karl H. (kbuchegg)


Lesenswert?

ccccccc schrieb:
> naja outPin ist vom Datentyp int. GPSEL0 wird nicht klar was das für ein
> Datentyp ist. Da es in der 2ten Klammer der vorletzten Zeile zu einer
> implizieten Typumwandlung kommt

Das ist Pointer Arithmetik. Das Ergebnis hat denselben Datentyp, wie ihn 
der Pointer hatte. Und der ist 'vaoltile uint32_t *'. Ganz ohne Cast.

: Bearbeitet durch User
von schlau-werden-Wollender (Gast)


Lesenswert?

Was mich auch ein bisschen eingeschüchtert hat ist die Klammerung:
1
(gpioBaseMem + GPFSEL0/4 + outPin/10)
Von der Arithmetik her könnte man die Klammer doch auch weglassen, oder?

von Karl H. (kbuchegg)


Lesenswert?

schlau-werden-Wollender schrieb:
> Was mich auch ein bisschen eingeschüchtert hat ist die Klammerung:
>
1
> (gpioBaseMem + GPFSEL0/4 + outPin/10)
2
>
> Von der Arithmetik her könnte man die Klammer doch auch weglassen, oder?

Dann wär der Cast noch viel unsinniger.
Denn ein Cast bindet am stärksten. Damit wäre
1
volatile uint32_t *fselAddr = (volatile uint32_t*)gpioBaseMem + GPFSEL0/4 + outPin/10;
dasselbe wie
1
volatile uint32_t *fselAddr = ((volatile uint32_t*)gpioBaseMem) + GPFSEL0/4 + outPin/10;

und da der Datentyp von gpioBaseMem bereits
1
    volatile uint32_t *gpioBaseMem, ...
ist, castet man eine Variable auf ihren eigenen Datentyp. Eine sinnlose, 
ja sogar gefährliche, Operation.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

ccccccc schrieb:
> Der Cast muss ja nicht unbedingt gemacht werden. Sprich
>
1
> (gpioBaseMem + GPFSEL0/4 + outPin/10)
2
>
> kann auch den richtigen Datentyp zurückgeben, das weiß ich jetzt nicht
> auswendig. Allerdings ist es schöner das ganze nochmal explizit
> darzustellen,

Das Problem an dieser Stelle ist dasselbe wie bei jedem Cast, bei dem 
ein Pointer umgecastet wird: Es ist eine Waffe, weil man de facto 
jegliche Typprüfung des Compilers damit abschaltet.
So etwas macht man nicht, wenn man nicht unbedingt muss!

> durchaus nochmal machen. Nach dem Python-Grundsatz: "Explicit is better
> than implicit"

... but not, if you tell the compiler: I don't care if I make a mistake. 
I want you to just shut up!

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

schlau-werden-Wollender schrieb:
> Autor: ccccccc (Gast) schrieb:
>>GPSEL0 wird nicht klar was das für ein
>>Datentyp ist.
>
> Das gehört noch dazu:
>
1
> #define GPFSEL0   0x00000000 //gpio function select. There are 6 of 
2
> these (32 bit registers)
3
>
>
> Sehe ich das richtig: Du meinst, daß:
> int outPin = 10;
> den Zeiger hier;
> uint32_t *gpioBaseMem
> auf ein int castet?


Jungs.
Ihr müsst schon die C Regeln lernen, wenn ihr C programmieren wollt!
Wie Pointer-Arithmetik in C funktioniert, ist klar geregelt. Ohne 
Pointer Arithmetik bleiben dann auch die Feinheiten von Arrays bzw. 
Arrayzugriffe auf immer ein Buch mit 7 Siegeln.

: Bearbeitet durch User
von mameluk (Gast)


Lesenswert?

[quote]
und da der Datentyp von gpioBaseMem bereits

    volatile uint32_t *gpioBaseMem, ...

ist, castet man eine Variable auf ihren eigenen Datentyp. Eine sinnlose,
ja sogar gefährliche, Operation.[/quote]
Sinnlos und überflüssig auf jeden Fall, aber wieso gefährlich?

von Karl H. (kbuchegg)


Lesenswert?

mameluk schrieb:
> [quote]
> und da der Datentyp von gpioBaseMem bereits
>
>     volatile uint32_t *gpioBaseMem, ...
>
> ist, castet man eine Variable auf ihren eigenen Datentyp. Eine sinnlose,
> ja sogar gefährliche, Operation.[/quote]
> Sinnlos und überflüssig auf jeden Fall, aber wieso gefährlich?

Denk mal darüber nach, welche Falle sich auftut, wenn sich Datentypen 
ändern.
Gut, ist in diesem Fall nicht sehr wahrscheinlich. Aber im allgemeinen 
Fall.
1
int* dataTable;
2
3
.... dataTable kriegt Speicher und Werte
4
5
6
void foo()
7
{
8
  int* myData = (int*)dataTable;
9
10
  *myData = 8;
11
}

So weit so gut. Nichts spezielles passiert hier. Der Cast ist zwar 
überflüssig, ist aber an und für sich kein Problem.

Die Zeit vergeht und in ein paar Monaten stellt sich raus, dass 
dataTable auf einen double erweitert werden muss. Kein Problem, der 
Programmierer ändert den Datentyp
1
double* dataTable;
2
3
.... dataTable kriegt Speicher und Werte

und auf die Funktion vergisst er, bzw. das weiss er alles nicht mehr. Da 
steht nach wie vor
1
void foo()
2
{
3
  int* myData = (int*)dataTable;
4
5
  *myData = 8;
6
}

Tja. Und das geht in die Hose. Ohne Cast
1
void foo()
2
{
3
  int* myData = dataTable;
4
5
  *myData = 8;
6
}
hätte es vom Compiler einen Error gegeben. Denn links vom = steht der 
Datentyp 'Pointer auf int', rechts davon steht 'Pointer auf double' und 
die beiden können nicht einfach so zugewiesen werden, weil sie nicht 
kompatibel sind.
Mit dem Cast hat man aber dem Compiler effektiv gesagt: "Ich weiss, dass 
die Datentypen links und rechts nicht zusammen stimmen, das macht aber 
nichts. Ich bin der Programmierer und du hältst die Klappe!"
Und genau das macht der Compiler auch. Anstatt einen offensichtlichen 
Fehler zu melden (und die richtige Aktion wäre es natürlich den Datentyp 
von myData auf 'Pointer auf double' zu ändern) akzeptiert der Compiler 
das fehlerhafte Statement gezwungenermassen.

Wenn ein Pointer umgecastet wird, dann wird damit effektiv die 
Datentypprüfung abgeschaltet! Am numerischen Wert des Pointers ändert 
sich nichts, aber ein anderer Datentyp verursacht bei Derefenzierung 
eine andere Aktion (bzw. bei Adressarithmetik), weil da implizit immer 
die sizeof dessen auftaucht, worauf der Pointer zeigt.
Die Typprüfung des Compilers willst du aber nicht abschalten. Gerade bei 
Pointern nicht. Wenn 2 Pointer nicht datentypkompatibel sind, dann ist 
das (abgesehen von den Fällen, in denen ein volatile dazu oder 
weggecastet wird) in 980 von 1000 Fällen ein tatsächlicher Fehler! 
Natürlich kommt es vor, dass man Pointer auch mal umcasten muss. 
Speziell dann, wenn man an die Bytedarstellung von irgendwas ran muss. 
Aber abgesehen davon ist das meistens ein Fehler. Daher: Wenn da etwas 
nicht stimmt, dann willst du haben, dass der Compiler Feuer schreit. Und 
daher willst du den Compiler nicht mit einem Cast ruhig stellen. Auch 
nicht vorsorglich. Du schneidest dich damit im schlechtesten Fall nur 
ins eigene Fleisch und im besten Fall bewirkt der Cast genau gar nichts 
(so wie hier).

Wir alle wissen, dass man sich in C sehr leicht selbst ins Knie 
schiessen kann. Du willst jede Hilfe und jede Überwachung vom Compiler 
haben, die du kriegen kannst. Die mutwillig einfach abzuschalten, ist 
das genaue Gegenteil davon.

: Bearbeitet durch User
von mameluk (Gast)


Lesenswert?

Danke für die ausführliche Antwort! :) Auf der Ebene war mir das Problem 
klar, ich dachte im vorliegenden Fall hätte ich irgendeine spezielle 
Falle übersehen.

Karl Heinz schrieb:
> Wir alle wissen, dass man sich in C sehr leicht selbst ins Knie
> schiessen kann. Du willst jede Hilfe und jede Überwachung vom Compiler
> haben, die du kriegen kannst. Die mutwillig einfach abzuschalten, ist
> das genaue Gegenteil davon.

Jepp!

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.