Forum: Mikrocontroller und Digitale Elektronik Präprozessor Verständnis


von elGasto (Gast)


Lesenswert?

Hi,

Ich habe eine solche Präprozessor-Anweisung geschrieben:
1
#define LUT_FACTOR(s32)   ((TABLE_SIZE-1) / (3.141592654/2)*ONE + 0.5)

Das Programm enthält später den richtigen Wert, das hab ich überprüft.

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.
Dann verstehe ich allerdings nicht warum man #error bei bestimmten 
Werten erzeugen kann, schließlich wird der Präprozessor nur einmal ganz 
am Anfang aufgerufen.

Wie genau rechnet der Präprozessor/Compiler, bzw. wie genau kann man 
z.B. Pi angeben? Sind das floats? Was für welche?

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?

Viele Grüße

von Timmo H. (masterfx)


Lesenswert?

Der Präprozessor fügt einfach nur den Text ein.
Der Compiler optimiert es dann so, dass die Berechnungen schon vorher 
gemacht werden und im Quellcode dann nur noch eine Konstante steht, 
damit das nicht zu lLaufzeit passiert.

Beispiel:
#define TEST(x) 1024/4 + 10*(x)

Im Quellcode wird TEST(10) also durch 1024/4 + 10*(10) ersetzt

Der Compilier sieht, dass es alles Konstanten sind und macht daraus den 
Wert 356.
1
#define TEST(x) 1024/4 + 10*(x)
2
3
int main(){
4
  int a;
5
  a = TEST(10);
6
}

wird zu
1
int main(){
2
  int a;
3
  a = 356;
4
}

von yalu (Gast)


Lesenswert?

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

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.