Hallo zusammen,
ich frage mich gerade ob es möglich ist, durch den Präprozessor
herauszufinden, ob char signed oder unsigned ist.
Hintergrund ist, daß ich den gleichen Code für MSVC (dort ist char
signed) und für den GCC (char ist unsigned) nutze und mit einer
#if/#endif die passenden Programmteile einkompilieren will.
Die einfachste Idee
1
#if ((char) -1 < 0)
2
...
3
#endif
funktioniert nicht, da der Präprozessor ja keinen Cast kennt. Eine
Definition CHAR_MIN scheint auch nicht auf beiden Compilern verfügbar.
Die brutale Variante
1
#if _MSC_VER
2
...
3
#endif
würde ich gern vermeiden.
Läßt sich das überhaupt durch den Präprozessor feststellen?
Viele Grüße
W.T.
Walter Tarpan schrieb:> Hintergrund ist, daß ich den gleichen Code für MSVC (dort ist char> signed) und für den GCC (char ist unsigned) nutze und mit einer> #if/#endif die passenden Programmteile einkompilieren will.
wo spielt denn das eine rolle?
Und wenn es wirklich eine rolle spielt lege doch einfach fest wie dein
char sein soll.
unsigned char
Der bessere Weg wäre, gleich die richtigen Datentypen zu verwenden.
char für Zeichenketten
unsigned char für kleine Zahlen ohne Vorzeichen
signed char für kleine Zahlen mit Vorzeichen
Das funktioniert mit jedem Compiler ...
Walter Tarpan schrieb:> Hallo zusammen,> ich frage mich gerade ob es möglich ist, durch den Präprozessor> herauszufinden, ob char signed oder unsigned ist.
Ich muß mich da meinen Vorrednern anschließen. Es gibt eigentlich keinen
sinnvollen Grund, das wissen zu müssen. Wenn es für dein Programm
wichtig ist, hast du was falsch gemacht und solltest das ändern.
> Hintergrund ist, daß ich den gleichen Code für MSVC (dort ist char> signed) und für den GCC (char ist unsigned) nutze und mit einer> #if/#endif die passenden Programmteile einkompilieren will.
Wofür compilierst du denn? Bei den meisten Zielplattformen ist char bei
GCC meines Wissens auch signed.
> Eine Definition CHAR_MIN scheint auch nicht auf beiden Compilern> verfügbar.
Sollte sie aber eigentlich sein, denn die ist Pflicht.
Dr. Sommer schrieb:> Der Preprocessor macht eine Textersetzung und hat keine Ahnung von> "char" und "signed".
Das stimmt nicht ganz. Er muss #if Ausdrücke mit den Regeln der
Zielplattform auswerten können.
Hallo zusammen,
Rolf Magnus schrieb:> Wenn es für dein Programm> wichtig ist, hast du was falsch gemacht und solltest das ändern.
in meinem Fall hat dieser Satz voll zugetroffen. Nach einem fabelhaften
Mittagessen war der Kopf frei genug zu erkennen, daß ein einfacher Cast
in uint8_t das Problem sofort löst.
Luther Blissett schrieb:> #if '\000'-1 < 0> #warning "char ist signed"> #else> #warning "char is unsigned"> #endif
Und diese schöne Lösung merke ich mir mal für den Fall, daß es wirklich
gebraucht wird.
Vielen Danke und noch einen schönen Sonntag
W.T.
Nein, der Praeprozessor rechnet einfach mit int oder long long
Er kennt kein Char oder double usw.
Bei GCC kannst du doch spezifizieren was du haben willst,
-funsigned-char
-fsigned-char
letzteres von beiden im Makefile definieren und du hast dasselbe wie
MVC.
Luther Blissett schrieb:> #if '\000'-1 < 0
Der Präprozessor arbeitet nicht notwendigerweise mit dem Typensystem des
Compilers. Der richtige Weg ist sowas wie
#include <limits.h>
#if CHAR_MIN < 0
Im Compiler wiederum wird bei 8-Bit chars und
if ('\000'-1 < 0)
printf("signed\n");
else
printf("unsigned\n");
immer "signed" rauskommen, weil '\000'-1 als int berechnet wird und
folglich immer -1 ist, auch wenn chars unsigned sind.
Interessanterweise kommt bei gcc (und clang) tatsächlich das raus, was
du erwartest, d.h. bei -funsigned-char wirds unsigned. Aber grad das
zeigt, dass das Typensystem des Präprozessors nicht mit dem des
Compilers identisch ist. Und zudem gilt das nicht für jeden
Compiler(Präprozessor).
C99 legt für die Auswertung von #if Ausdrücken fest, dass die Konstanten
als die jeweilligen intmax_t und uintmax_t Werte verwendet werden
sollen.
1
#if ('\000'-1 < 0)
bedeutet also
1
#if ((uintmax_t)'\000'-(intmax_t)1<(intmax_t)0)
, wenn char unsigned ist.
Ich müsste mal nachgucken, ob intmax_t grösser als uintmax_t sein kann.
In diesem Fall würde der Test ja nicht mehr funktionieren!. Wie auch
immer, sich auf solche subtilen Auswertungsdetails in seinem Code zu
verlassen zu müssen ist immer schwierig. CHAR_MIN ist da wesentlich
einfacher und limits.h ist ein header der auch in freestanding
Environments das sein muss. Wenn der nicht da ist, dann ist der Compiler
schwer kaputt.
Walter Tarpan schrieb:> Hintergrund ist, daß ich den gleichen Code für MSVC (dort ist char> signed) und für den GCC (char ist unsigned) nutze und mit einer> #if/#endif die passenden Programmteile einkompilieren will.
Die einfachste "Fix" für solch fehlerhaften Code wäre die Compileroption
"-fsigned-char" für den gcc, oder die entsprechende unsigned-option für
den MSVC. Nur, weil dieunterschiedliche default-Optionen haben, heisst
das ja nicht, daß man die nicht ändern kann.
Aber wie weiter oben schon geschrieben wurde, behebt das nur die
Symptome, nicht die Ursache.
Oliver
Luther Blissett schrieb:> Ich müsste mal nachgucken, ob intmax_t grösser als uintmax_t sein kann.
Ich denke schon. Ein Compiler kann auch Integer-Typen und -Konstanten
unterstützen, die nicht vom Standard abgedeckt sind und die einen
grösseren Wertebereich zulassen. Und dann wird ihm wohl nichts anderes
übrig bleiben, als intmax_t und uintmax_t entsprechend zu definieren. Es
muss davon jedoch nicht beide Vorzeichenvarianten geben. Allerdings
landet man bei solchen Fisematentchen schnell im Morast, denn auf die
strikte Konformität solcher Extreme ist wenig Verlass.
Aber die Vorzeicheneigenschaft von chars in #if expressions ist ebenso
"implementation defined" wie die Frage, ob der Zeichencode des
Präprozessors mit dem des Compilers identisch ist. Also ob
#if 'z' - 'a' == 25
if ('z' - 'a' == 25)
zum gleichen Ergebnis führt. Die Aussage des Präprozessors ist folglich
wohl nicht als Aussage über den Compiler zu gebrauchen.
Normalerweise definiert das Compiler-Steuerprogramm (cc, gcc, pcc, lcc,
...), das die diversen Phasen wie Präprozessor, Compiler, Assembler und
Linker aufruft, einen spezifischen Makro. Sowas wie "__UNSIGNED_CHAR__".
Includes wie limits.h nutzen dies - meistens.
/* Minimum and maximum values a `char' can hold. */
2
# ifdef __CHAR_UNSIGNED__
3
# define CHAR_MIN 0
4
# define CHAR_MAX UCHAR_MAX
5
# else
6
# define CHAR_MIN SCHAR_MIN
7
# define CHAR_MAX SCHAR_MAX
8
# endif
Heute ist der Präprozessor mitunter kein separates Programm mehr, auch
wenns "cpp" noch gibt, sondern im Compiler integriert. Dann muss das
Steuerprogramm das nicht erledigen und du musst im Compiler suchen:
1
$ strings /usr/lib/gcc/i686-linux-gnu/4.7/cc1|grep SIGNED
Ich finde CHAR_MIN < 0 eleganter, aber das ist Geschmacksache. Dieses
Compiler-Makro hatte ich auch nicht als Empfehlung gebracht, sondern als
Reaktion auf das ziemlich verunglückte limits.h von MinGW.