Forum: Compiler & IDEs Im Präprozessor herausfinden, ob char signed ist


von Walter T. (nicolas)


Lesenswert?

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.

von Peter II (Gast)


Lesenswert?

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

von Hans (Gast)


Lesenswert?

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

von Rolf Magnus (Gast)


Lesenswert?

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.

von Nase (Gast)


Lesenswert?

CHAR_MAX mit UCHAR_MAX und SCHAR_MAX vergleichen. Siehe limits.h

von Dr. Sommer (Gast)


Lesenswert?

Der Preprocessor macht eine Textersetzung und hat keine Ahnung von 
"char" und "signed". Es geht aber so:
1
#include <limits>
2
3
bool isSigned = std::numeric_limits<char>::is_signed ();
Der Rückgabewert ist constexpr und somit zur Compilezeit bekannt, kann 
daher z.B. für Template-Argumente verwendet werden.

von Luther B. (luther-blissett)


Lesenswert?

Geht doch ganz einfach:
1
$ gcc -c mx.c 
2
mx.c:2:2: warning: #warning "char ist signed" [-Wcpp]
3
4
$ arm-none-eabi-gcc -c mx.c 
5
mx.c:4:2: warning: #warning "char is unsigned" [-Wcpp]

Code:
1
#if '\000'-1 < 0 
2
#warning "char ist signed"
3
#else
4
#warning "char is unsigned"
5
#endif

von Luther B. (luther-blissett)


Lesenswert?

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.

von Walter T. (nicolas)


Lesenswert?

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.

von chris (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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

von Luther B. (luther-blissett)


Lesenswert?

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.

von Oliver (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

In limits.h von MinGW (war bei Code::Blocks dabei) steht
1
/* TODO: Is this safe? I think it might just be testing the preprocessor,
2
 *       not the compiler itself... */
3
#if  ('\x80' < 0)
4
#define CHAR_MIN  SCHAR_MIN
5
#define CHAR_MAX  SCHAR_MAX
6
#else
7
#define CHAR_MIN  0
8
#define CHAR_MAX  UCHAR_MAX
9
#endif

von (prx) A. K. (prx)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

hmmm. beim GCC (4.8) kann ich sowas nicht finden.  Keine Makro-name 
enthält "SIGNED".

von (prx) A. K. (prx)


Lesenswert?

Linux mint, limits.h:
1
/* 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
2
__CHAR_UNSIGNED__
3
__WCHAR_UNSIGNED__
4
UNSPEC_XOP_UNSIGNED_CMP
5
6
$ strings /usr/bin/clang|grep SIGNED
7
__CHAR_UNSIGNED__
8
__WCHAR_UNSIGNED__
9
__WINT_UNSIGNED__
10
_CHAR_UNSIGNED

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ok, hab's gefunden.  Hier aus der ältlichen 3.4.0 Doku zum cpp.  Damit 
ist es dann auch in neueren Versionen enthalten:
1
__CHAR_UNSIGNED__ 
2
   GCC defines this macro if and only if the data type char is unsigned
3
   on the target machine. It exists to cause the standard header file
4
   limits.h to work correctly. You should not use this macro yourself;
5
   instead, refer to the standard macros defined in limits.h.

http://gcc.gnu.org/onlinedocs/gcc-3.4.0/cpp/Common-Predefined-Macros.html

Die Lösung ist also wie gesagt 
Beitrag "Re: Im Präprozessor herausfinden, ob char signed ist"

von (prx) A. K. (prx)


Lesenswert?

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.

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.