> Aber wo genau wird dieser Wert berechnet? Der Präprozessor fügt nur
> stumpf ein, soweit ich das verstanden habe und übergibt Rechnungen an
> den Compiler.
Es gibt einen Fall, wo auch der Präprozessor rechnet, nämlich in
#if-Direktiven. Aber auch dort wird nur mit Integer und nicht mit
Float-Zahlen gerechnet. Die #error-Ausgabe steht meistens in einem
#if-Block. In allen anderen Fällen macht der Präprozessor nur
Textexpansion. Für die Auswertung konstanter Ausdrücke ist dann der
Compiler zuständig.
> Wie genau rechnet der Präprozessor/Compiler, bzw. wie genau kann man
> z.B. Pi angeben? Sind das floats? Was für welche?
Präprozessor: s.o.
Compiler: Die Genauigkeit ist die gleiche wie später bei der Ausführung
des fertigen Programms auf dem Zielsystem. Gleitkommakonstanten sind
defaultmäßig vom Typ double. Auf dem PC ist das double 64 Bit groß, auf
dem AVR nur 32 Bit. Entsprechend berechnet der PC-GCC konstante
Ausdrücke genauer als der AVR-GCC, selbst wenn dieser auf dem gleichen
PC installiert ist. Maßgeblich für die Genauigkeit ist also immer das
Zielsystem und nicht der Rechner auf dem kompiliert wird.
> Darüber hinaus würde ich gerne sicher gehen, dass die Rechnung nicht
> womöglich in meinem uC (zur Laufzeit) ausgeführt wird, aber ich schätze
> da brauche ich mir keine Sorgen zu machen, oder?
Wenn Ausdrücke oder Teilausdrücke ohne Umformung des Terms als konstant
erkannt werden können, werden sie praktisch von jedem Compiler zur
Compilezeit ausgerechnet und das auch ohne eingeschaltete Optimierung.
Der Compiler tut dies, weil er in bestimmten Fällen konstante Ausdrücke
ohne auswerten muss, bspw. in
int array[5*4+1];
Da bedeutet es für den Compilerentwickler keinen großen Zusatzaufwand,
diese Form der Auswertung auf alle Ausdrücke anzuwenden. Viele
Compiler können zusätzlich einfache Umformungen vornehmen, um dadurch
noch größere Teile von Ausdrücken vorab auswerten zu könenn, darauf
würde ich mich aber nicht verlassen.
Beispiele:
Folgendes sollte praktisch jeder Compiler können:
a=3+4*5; -> a=23;
a=1+2+x; -> a=3+x;
a=x*(1+2) -> a=x*3;
Folgendes können viele, aber möglicherweise nicht alle, da dazu die
Operanden umgestellt werden müssen.
a=2-(x+1); -> a=1-x;
Folgendes kann möglicherweise ebenfalls nicht jeder:
a=x+1+2; -> a=x+3;
Der Ausdruck ist gleichbedeutend mit (x+1)+2, es gibt also keine
konstanten Teilausdrücke. Besser macht man die Umformung selber und
schreibt:
a=x+(1+2); oder a=1+2+x;
Dies sollte wiederum von jedem Compiler in x+3 umgesetzt werden können.
Jeder Teilausdruck in diesen Beispielen kann natürlich auch durch einen
entsprechenden Präprozessormakroaufruf ersetzt werden, also könnte das
erste Beiepiel auch so lauten:
#define ZWANZIG (4*5)
a = 3+ZWANZIG;
Wichtig ist, dass in der Makrodefinition die Ausdrücke immer in Klammern
gesetzt sind (wie in (4*5) ). Zum einen können ohne die Klammern bei der
Expansion falsche Ergebnisse entstehen, zum anderen kann der Compiler so
am leichtesten erkennen, dass der Ausdruck konstant ist, auch dann, wenn
er als Teilausdruck eines größeren Ausdrucks auftaucht.