Abrufen des Werts von Ausdrücken zur Kompilierzeit in C

Lesezeit: 7 Minuten

Benutzer-Avatar
Jason S

Gibt es eine Möglichkeit, den C-Compiler (in meinem Fall XC16, der auf gcc basiert) die Ergebnisse von Ausdrücken zur Kompilierzeit ausgeben zu lassen?

Wir haben viele #defines wie

#define FOO 37.6
#define FOO_BASE 0.035
#define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE)

und ich würde gerne die tatsächlichen Zahlen wissen, auf die der Compiler diese Ausdrücke reduziert.

Beachten Sie, dass dies der Fall ist NICHT dasselbe wie wissen zu wollen, was der Präprozessor ausgibt; Der Präprozessor berechnet keine Mathematik, er ersetzt nur Dinge. Wenn ich mir die Ausgabe des Präprozessors ansehe, bekomme ich (effektiv)

#define FOO_FIXEDPOINT (int16_t)(37.6/0.035)

und es ist die Compilernicht der Präprozessor, der einen Wert ermittelt.


Ein weiterer wichtiger Punkt hier: Ich habe die Freiheit, eine spezielle .c-Datei zu erstellen, die solche Dinge tut

#include "foo.h"

const int16_t foo_fixedpoint = FOO_FIXEDPOINT;

damit ich dem Compiler einen Ort zur Verfügung stelle, an dem er seine Arbeit erledigen und die Ergebnisse als Konstante setzen kann.


Nur als vollständig eigenständiges Beispiel, wenn ich dies in foo.c einfüge und ausführe xc16-gcc.exe -E foo.c:

#include <stdint.h>

#define FOO 37.6
#define FOO_BASE 0.035
#define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE)

int main()
{
   int x = FOO_FIXEDPOINT;
}

Ich bekomme diese Ausgabe:

[... typedefs elided ...]
int main()
{
   int x = (int16_t)(37.6/0.035);
}

  • Bitte senden Sie mir kein RTFM. Dazu muss ich wissen, wonach ich suchen muss.

    – Jason S

    30. Juni 2015 um 22:14 Uhr

  • @iharob: Der Präprozessor berechnet keine Zahlen, er führt lediglich eine Substitution durch. In diesem Beispiel würde ich also #define FOO_FIXEDPOINT (int16_t)(37.6/0.035) erhalten, was mir nicht weiterhilft. Ich will das Ergebnis dieses Ausdrucks.

    – Jason S

    30. Juni 2015 um 22:16 Uhr


  • NEIN, ES IST KEIN Duplikat. Ich möchte die #defines nicht ausgeben, ich möchte wissen, worauf der Compiler Ausdrücke zur Kompilierzeit reduziert.

    – Jason S

    30. Juni 2015 um 22:18 Uhr

  • Die Frage ist, ob es eine Art Compiler-Schalter gibt, der Werte ausgibt, die auf Konstanten zur Kompilierzeit reduziert werden, wie z sizeof(20) oder 40/3.75. Der Präprozessor führt dies nicht durch. Zum Beispiel, #define FOO_FIXEDPOINT (int16_t)(FOO/FOO_BASE) ersetzt alle Instanzen von FOO_FIXEDPOINT mit dem Text (int16_t)(37.6/0.035)die vom Compiler auf eine Kompilierzeitkonstante reduziert wird.

    Benutzer539810

    30. Juni 2015 um 22:22 Uhr


  • Danke. Und bitte geben Sie den Leuten in Zukunft ein wenig mehr Vertrauen, bevor Sie schließen.

    – Jason S

    30. Juni 2015 um 22:25 Uhr

Schauen Sie sich die Flaggen an -fdump-tree-* in dem Debugging-Optionen Seite, um die Ausgabe der Zwischensprache zu sehen (etwas auf niedrigem Niveau, aber gut lesbar).

Sie können verwenden -fdump-tree-all um die Datei in verschiedenen Stadien zu sehen, aber wahrscheinlich ist alles, was Sie brauchen, die -fdump-tree-original. Schauen Sie unten in der generierten *.original Datei, um nach Ihrer Hauptfunktion zu suchen:

...
;; Function main (null)
;; enabled by -tree-original

{
  int x = 1074;

    int x = 1074;
}

  • Hmm. Noch eine Frage. Warum funktioniert das so? Wenn es sich um den “ursprünglichen” AST handelt, wie kommt es, dass die konstante Faltung vor diesem Punkt erfolgt?

    – Jason S

    1. Juli 2015 um 0:26 Uhr

  • @JasonS Ich weiß nicht, wie, aber grundlegende Ausdrücke (z. B. 3 + 4) scheinen viel früher vor der Baumoptimierungsphase ausgewertet zu werden.

    Benutzer1129665

    1. Juli 2015 um 2:14 Uhr

Wie in den Kommentaren besprochen, ist es einfach, ein Programm zu erstellen, das ihre Werte ausgibt, insbesondere wenn die numerischen Makros nicht mit Makros gemischt werden, die nicht numerische Typen haben.

Obwohl Ihre Umgebung ein Cross-Compiler ist, ist dies eine nützliche Übung, da alle gccs konstante Ausdrücke intern mit demselben Code verarbeiten. Dies führt Berechnungen mit erweiterter Genauigkeit durch, sodass kompilierte Konstanten innerhalb eines ULP des genauen Werts liegen.

Daher sollte jeder gcc eine ziemlich genaue Vorstellung davon vermitteln, was in Ihren Code einfließt.

Perl:

print "#include<stdio.h>\n";
print "#include \"$ARGV[0]\"\n";
print "#define S(X) #X\n";
print "int main(void) {\n";
open F, $ARGV[0] or die $!;
while (<F>) {
  print "  printf(\"%s=%.13g\\n\", S($1), (double)$1);\n" if /#define\s+(\w+)\s+\S/;
}
print "  return 0;\n}\n";

Probieren Sie es jetzt aus, indem Sie weiterlaufen math.h.

perl /usr/include/math.h > math_h_syms.c

Dies erzeugt:

#include<stdio.h>
#include "/usr/include/math.h"
#define S(X) #X
int main(void) {
  printf("%s=%.13g\n", S(INFINITY), (double)INFINITY);
  printf("%s=%.13g\n", S(FP_NAN), (double)FP_NAN);
  printf("%s=%.13g\n", S(FP_INFINITE), (double)FP_INFINITE);
  printf("%s=%.13g\n", S(FP_ZERO), (double)FP_ZERO);
  printf("%s=%.13g\n", S(FP_NORMAL), (double)FP_NORMAL);
  printf("%s=%.13g\n", S(FP_SUBNORMAL), (double)FP_SUBNORMAL);
  printf("%s=%.13g\n", S(FP_SUPERNORMAL), (double)FP_SUPERNORMAL);
  printf("%s=%.13g\n", S(FP_ILOGB0), (double)FP_ILOGB0);
  printf("%s=%.13g\n", S(FP_ILOGBNAN), (double)FP_ILOGBNAN);
  printf("%s=%.13g\n", S(MATH_ERRNO), (double)MATH_ERRNO);
  printf("%s=%.13g\n", S(MATH_ERREXCEPT), (double)MATH_ERREXCEPT);
  printf("%s=%.13g\n", S(math_errhandling), (double)math_errhandling);
  printf("%s=%.13g\n", S(M_E), (double)M_E);
  printf("%s=%.13g\n", S(M_LOG2E), (double)M_LOG2E);
  printf("%s=%.13g\n", S(M_LOG10E), (double)M_LOG10E);
  printf("%s=%.13g\n", S(M_LN2), (double)M_LN2);
  printf("%s=%.13g\n", S(M_LN10), (double)M_LN10);
  printf("%s=%.13g\n", S(M_PI), (double)M_PI);
  printf("%s=%.13g\n", S(M_PI_2), (double)M_PI_2);
  printf("%s=%.13g\n", S(M_PI_4), (double)M_PI_4);
  printf("%s=%.13g\n", S(M_1_PI), (double)M_1_PI);
  printf("%s=%.13g\n", S(M_2_PI), (double)M_2_PI);
  printf("%s=%.13g\n", S(M_2_SQRTPI), (double)M_2_SQRTPI);
  printf("%s=%.13g\n", S(M_SQRT2), (double)M_SQRT2);
  printf("%s=%.13g\n", S(M_SQRT1_2), (double)M_SQRT1_2);
  printf("%s=%.13g\n", S(MAXFLOAT), (double)MAXFLOAT);
  printf("%s=%.13g\n", S(FP_SNAN), (double)FP_SNAN);
  printf("%s=%.13g\n", S(FP_QNAN), (double)FP_QNAN);
  printf("%s=%.13g\n", S(HUGE), (double)HUGE);
  printf("%s=%.13g\n", S(X_TLOSS), (double)X_TLOSS);
  printf("%s=%.13g\n", S(DOMAIN), (double)DOMAIN);
  printf("%s=%.13g\n", S(SING), (double)SING);
  printf("%s=%.13g\n", S(OVERFLOW), (double)OVERFLOW);
  printf("%s=%.13g\n", S(UNDERFLOW), (double)UNDERFLOW);
  printf("%s=%.13g\n", S(TLOSS), (double)TLOSS);
  printf("%s=%.13g\n", S(PLOSS), (double)PLOSS);
  return 0;
}

Kompilieren und ausführen:

INFINITY=inf
FP_NAN=1
FP_INFINITE=2
FP_ZERO=3
FP_NORMAL=4
FP_SUBNORMAL=5
FP_SUPERNORMAL=6
FP_ILOGB0=-2147483648
FP_ILOGBNAN=-2147483648
MATH_ERRNO=1
MATH_ERREXCEPT=2
math_errhandling=2
M_E=2.718281828459
M_LOG2E=1.442695040889
M_LOG10E=0.4342944819033
M_LN2=0.6931471805599
M_LN10=2.302585092994
M_PI=3.14159265359
M_PI_2=1.570796326795
M_PI_4=0.7853981633974
M_1_PI=0.3183098861838
M_2_PI=0.6366197723676
M_2_SQRTPI=1.128379167096
M_SQRT2=1.414213562373
M_SQRT1_2=0.7071067811865
MAXFLOAT=3.402823466385e+38
FP_SNAN=1
FP_QNAN=1
HUGE=3.402823466385e+38
X_TLOSS=1.414847550406e+16
DOMAIN=1
SING=2
OVERFLOW=3
UNDERFLOW=4
TLOSS=5
PLOSS=6

  • @JasonS: naja nein. math.h definiert diese als “Rückgabewerte für fpclassify” (obwohl meine lokalen Mingw-Werte unterschiedlich sind).

    – Jongware

    1. Juli 2015 um 8:40 Uhr

Benutzer-Avatar
Valent H

BEARBEITEN Das geht nicht 🙁

Auch bei dem Trick, Integer in einen String umzuwandeln, wird der zusammengesetzte Wert nicht vom Präprozessor ausgewertet.

#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

#define A 1
#define B 2
#define C A+B

#pragma message("A=" STR(A))
#pragma message("B=" STR(B))
#pragma message("C=" STR(C))

Compilerausgabe (VS2008):

1>A=1
1>B=2
1>C=1+2

Der Präprozessor hilft hier also nicht.

Ursprüngliche ANTWORT
Als letzter Ausweg, wenn es keine Möglichkeit gibt, die Werte aus den Zwischendateien zu bekommen, mittels gcc-Optionen usw.

Ich würde die Quelldateien für #define grep, die Ausgabe in eine neue .c-Datei umleiten und #define durch ersetzen #pragma message. Der Aufruf von gcc für diese Datei würde alle Definitionen auflisten. Natürlich vorausgesetzt, Ihr Compiler unterstützt #pragma message.

  • hmm. Dadurch kann der Compiler mathematische Berechnungen durchführen und die Ergebnisse als Nachricht ausgeben?!

    – Jason S

    30. Juni 2015 um 22:44 Uhr

  • Leider funktioniert die Pragma-Nachricht nur mit Zeichenfolgen, und das OP fragt nach berechneten numerischen Werten. Eigentlich müsste er (für Fließkommakonstanten) printf(“%f\n”, MACRO) kompilieren;

    – Gen

    30. Juni 2015 um 22:58 Uhr

  • ah … ja, und die Verwendung von printfs ist eine hässliche Sache, weil dies ein Cross-Compiler für ein eingebettetes System ist.

    – Jason S

    30. Juni 2015 um 23:02 Uhr

  • Oh, wie peinlich, stimmt. pragma message hilft nicht bei numerischen Konstanten, es werden nur Strings ausgegeben. Ok, was bleibt, ist eine Liste von printfs zu erstellen. Natürlich ist es nicht so einfach und wird nur zur Laufzeit etwas drucken.

    – Valent H

    30. Juni 2015 um 23:05 Uhr

1159370cookie-checkAbrufen des Werts von Ausdrücken zur Kompilierzeit in C

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy