Forum: Mikrocontroller und Digitale Elektronik Rangordnung C Operatoren


von C_NonExpert (Gast)


Lesenswert?

Hallo zusammen,

ich verstehe gerade nicht die Rangordnung von C operatorn:

Laut Wikipedia (und andere) hat '[]' vor dem '->' Vorrang.

Wie weise ich z.B. die Zahl 12 im Beispiel richtig zu?

----------------
struct TS
{
  int a[10];
}
...
// Pointer auf TS;
TS* p_TS;
...
// Variante a (so habe ich es immer gemacht
p_TS->a[2] = 12;

// Variante b
(p_TS->a)[2] = 12;
------------------

Beides funktioniert. Logisch Sinn macht vermutlich auch beides, da "[]" 
ohne Bezug keinen Sinn macht. Variante b ist laut Wiki richtig.
Was sagt der Compiler?

Grüße
Circe

von (prx) A. K. (prx)


Lesenswert?

C_NonExpert schrieb:

> Laut Wikipedia (und andere) hat '[]' vor dem '->' Vorrang.

Konkrete Belege?

Soweit mir bekannt sind sie in der gleichen Vorranggruppe, daher gilt 
die Assoziativität, und die sagt "links vor rechts".

von Floh (Gast)


Lesenswert?

C_NonExpert schrieb:
> Was sagt der Compiler?
Ich denke, in diesem Fall ist es egal.

TS->a[2]
macht ja nix anderes, als die Adresse des Pointers + Offest zur Variable 
a in der Struktur + Index im Array zusammenzurechnen.
Was jetzt zuerst zusammengerechnet wird, spielt meiner Meinung nach 
keine Rolle.
So denke ich mir es jedenfalls. :-)

von C_NonExpert (Gast)


Lesenswert?

Jepp, danke. Du hasst recht!

von (prx) A. K. (prx)


Lesenswert?

Floh schrieb:

>> Was sagt der Compiler?
> Ich denke, in diesem Fall ist es egal.

Ist es nicht. Hätte [] wirklich Vorrang, dann würde "a" der Bezug zur 
struct fehlen und der Name würde nicht als struct member sondern als 
normale lokale oder globale Variable verstanden, erst das Resultat des 
[] Operators wäre dann die rechte Seite des -> Operators. Etwas mit dem 
der Compiler nichts anfangen könnte.

Eine nette Illustration hierfür ist die etwas ungebräuchliche 
Kommutativität des [] Operators, denn a[i] ist per Definition des [] 
Operators als *(a+i) identisch mit i[a].

von W.S. (Gast)


Lesenswert?

C_NonExpert schrieb:
> // Pointer auf TS;
> TS* p_TS;
> ...
> // Variante a (so habe ich es immer gemacht
> p_TS->a[2] = 12;

Ähem.. sollte es nicht eher so heißen:

struct TS* p_TS;

Und deine Version a
 p_TS->a[2] = 12;
halte ich als einzige Schreibversion für richtig und gut. Mag ja sein, 
daß man das Ganze auch verquer hinschreiben kann und trotzdem zufällig 
das Richtige bei herauskommt, aber alles Andere wäre unleserlich.

A. K. schrieb:
> Eine nette Illustration hierfür.. denn a[i] ist per Definition des []
Operators als *(a+i) identisch mit i[a]

Bitte?
Wenn man es schon per Pointerconstruct ausdrücken will, so doch eher so:
a[i] entspräche dann *(a + i*sizeof(a))
Immerhin soll a[i] ja das i'te Element von a bezeichnen und hier ist 
nichts über die Größe bzw. Datenart von a gesagt. Gelle?

W.S.

von Stefan E. (sternst)


Lesenswert?

W.S. schrieb:
> Wenn man es schon per Pointerconstruct ausdrücken will, so doch eher so:
> a[i] entspräche dann *(a + i*sizeof(a))
> Immerhin soll a[i] ja das i'te Element von a bezeichnen und hier ist
> nichts über die Größe bzw. Datenart von a gesagt. Gelle?

Unsinn. Du hast offenbar Nachholbedarf beim Thema "Pointer-Arithmetik". 
Schlag es nach.

von W.S. (Gast)


Lesenswert?

Stefan Ernst schrieb:
> Unsinn.

Ach ja? Anstatt hier nur herumzumotzen hättest du besser eine sachliche 
Erklärung oder Widerlegung abgeben können. Solange das fehlt, ist dein 
Beitrag nur heiße Luft.


W.S.

von (prx) A. K. (prx)


Lesenswert?

C99 sagt: "The definition of the subscript operator [] is that E1[E2] is 
identical to (*((E1)+(E2)))."

von Thomas D. (t0mmy)


Lesenswert?

Fakt ist, dass [i] nicht die Bytes hochzählt sondern in Strukturgrößen 
denkt.
*(a+i) Kann deshalb nicht ganz passen. *(a + i*sizeof(a)) sieht für mich 
sinnvoller aus.

von (prx) A. K. (prx)


Lesenswert?

Thomas Decker schrieb:

> *(a+i) Kann deshalb nicht ganz passen.

Doch, und zwar genau deswegen. Wenn man *(a+i) als C Ausdruck versteht, 
nicht als Rechenvorschrift in Maschinenadressen. a+1 zeigt auf das 2. 
Element, nicht auf das 2. Byte. Solche Pointer-Arithmetik multipliziert 
- auf Maschinenebene betrachtet - mit der Elementgrösse. Die 
Arbeitsweise des Index-Operators ist somit eine direkte Folge der 
Pointer-Arithmetik.

von Stefan E. (sternst)


Lesenswert?

Oder vielleicht nochmal in etwas anderer Kurzform:

Wenn a ein Pointer ist, dann zeigt a+1 auf das nächste Objekt seines 
Zieltyps, unabhängig davon, wie groß dieser Zieltyp konkret ist.

von W.S. (Gast)


Lesenswert?

Thomas Decker schrieb:
> *sizeof(a)

Ja, das ist ganz unten auf Maschinenebene exakt so, aber da der Compiler 
ja weiß, was man eigentlich meint, fügt er diesen Term "unter der Decke" 
von sich aus hinzu, so daß am Ende das Richtige dabei herauskommt. Man 
würde also (a+3) schreiben, aber im Grunde (a+3*sizeof(Typ von a)) 
meinen, da man ja das 4. Element, also a[3] tatsächlich meint.

Logisch ist das Mumpitz, da eine Integervariable i nicht addierbar ist 
zu einem Array a, obwohl der Ausdruck "a+i" für sich genommen völlig 
legal ist. Stattdessen ist all dieses eben eine Konvention, also eine 
Festlegung, die man beachten muß. C ist voll davon, allein die 
verschiedenen Bedeutungen des Zeichens * in verschiedenen Kontexten 
füllen ganze Bibeln...

Dies ist wieder mal ein Beispiel dafür, daß in C das, was man 
hinschreiben muß eben nicht mit dem übereinstimmt, was man eigentlich 
gemeint hat. Natürlich kann man sich so sehr an C gewöhnen, daß einem 
der innere Widersinn all dieser Konstrukte nicht mehr auffällt, man also 
quasi betriebsblind wird. Aber: Ist das wirklich gut?

W.S.

von Stefan E. (sternst)


Lesenswert?

W.S. schrieb:
> Dies ist wieder mal ein Beispiel dafür, daß in C das, was man
> hinschreiben muß eben nicht mit dem übereinstimmt, was man eigentlich
> gemeint hat.

Also ich bin fast geneigt, das wieder mit einem "Unsinn" zu beantworten. 
;-)

Gerade bei der Pointer-Arithmetik stimmt doch das Verhalten in der 
überwiegenden Mehrzahl der Fälle genau mit dem überein, was auch 
tatsächlich gewollt/gemeint ist. Wer ptr++ schreibt, will damit zum 
nächsten Element und nicht zum nächsten Byte (in der Regel).

von Sven P. (Gast)


Lesenswert?

W.S. schrieb:
> Dies ist wieder mal ein Beispiel dafür, daß in C das, was man
> hinschreiben muß eben nicht mit dem übereinstimmt, was man eigentlich
> gemeint hat. Natürlich kann man sich so sehr an C gewöhnen, daß einem
> der innere Widersinn all dieser Konstrukte nicht mehr auffällt, man also
> quasi betriebsblind wird. Aber: Ist das wirklich gut?
Es wäre in erster Linie gut, C nicht mit Halbwissen und Trial&Error zu 
erlernen, sondern von Anfang an in ein Buch zu schauen.

Wenn man zum Beispiel begreift, dass Zeiger keine Speicheradressen auf 
Maschinenebene oder im Arbeitsspeicher sind, dann sehen die ganzen 
'unlogischen' Konstrukte auf einmal ziemlich durchdacht aus.
Also bitte nicht etwas verurteilen, was man (noch) nicht ganz 
durchschaut und/oder begriffen hat.

Das soll kein Vorwurf sein, ich bin auch wunderbar mit Trial&Error aufs 
Maul gefallen.

Wenn du Mumpitz möchtest, dann geh doch mal zu C# oder Java oder 
irgendeiner Sprache, die keine Zeiger kennt, sondern stattdessen alles 
mit (möglicherweise sogar transparenten -> Java) Objektreferenzen 
vergewaltigt. Da hast du dann allen Grund, dir jedes Mal aufs Neue das 
Gehirn zu zerknoten, ob und wie und warum ein Objekt nun kopiert wird 
oder nicht... :-}
1
putchar("abcde"[2]);

von (prx) A. K. (prx)


Lesenswert?

W.S. schrieb:

> Dies ist wieder mal ein Beispiel dafür, daß in C das, was man
> hinschreiben muß eben nicht mit dem übereinstimmt, was man eigentlich
> gemeint hat.

Nur wenn du eigentlich in Assembler programmieren wolltest, aber 
versehentlich den C Compiler erwischt hast.

Als die Sprache C definiert wurde, da wurden in vielen Maschinen keine 
Bytes adressiert, sondern Maschinenworte. Was immer das war. Bytes waren 
dann Teile eines einzelnen Wortes. Infolgedessen waren dann die Adressen 
und damit die Pointer in ihrer Darstellung abhängig vom darauf zeigenden 
Datentyp (gibts wohl auch heute noch, im MaxQ2000). Deine Vorstellung 
hätte Pointer-Arithmetik dermassen maschinenabhängig gemacht, dass eine 
Portierung auf eine Maschine mit anderer Wort- und/oder Integer-Grösse 
fast unmöglich geworden wäre.

Man kann nun trefflich darüber streiten, ob der sehr freizügige Umgang 
von C mit Pointern eher nützlich oder eher Tretmine ist, aber wenn man 
mal akzeptiert, dass es sie gibt, dann ist diese Arithmetik durchaus 
sinnvoll definiert. Denn sie stellt genau die minimale Abstraktion dar, 
ohne die Programme vollständig zu strukturiertem Assembler mutiert 
wären. Es könnte sich vielleicht lohnen, dafür mal in die 
Vorgängersprachen B und BCPL reinzusehen.

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.