2009-09-11 18 views
7

Als Übung möchte ich ein Makro schreiben, das mir sagt, ob eine Integer-Variable signiert ist. Dies ist, was ich bis jetzt habe, und ich bekomme die Ergebnisse, die ich erwarte, wenn ich das auf einer Char-Variable mit gcc -fsigned-char oder -funsigned-char versuche.Wie kann ich feststellen, ob eine C-Ganzzahlvariable signiert ist?

#define ISVARSIGNED(V) (V = -1, (V < 0) ? 1 : 0) 

Ist das tragbar? Gibt es eine Möglichkeit, dies zu tun, ohne den Wert der Variablen zu zerstören?

+0

Dies ist ein seltsames Problem, aber ich bin mehr fasziniert von dem, was Sie für diese Information verwenden ist. Jede Chance teilen? –

+0

Aus diesem Grund hat C++ RTTI. :) –

+2

@jeffamaphone: Eigentlich leuchten hier Vorlagen in C++. – sbi

Antwort

4
#define ISVARSIGNED(V) ((V)<0 || (-V)<0 || (V-1)<0) 

nicht den Wert von V. verändern Der dritte Test behandelt den Fall, in dem V == 0.

Auf meinem Compiler (gcc/Cygwin) das funktioniert für int und long aber nicht für char oder short.

#define ISVARSIGNED(V) ((V)-1<0 || -(V)-1<0) 

macht auch den Job in zwei Tests.

+0

Das Beste, was ich bisher gesehen habe. Portabel, standardkonform, genau so weit ich sehen kann. –

+1

Unterscheidet nicht zwischen signed/unsigned short und char. Diese Typen werden bei der Auswertung eines Ausdrucks < == > zu int hochgestuft. – mob

+0

Wenn Sie möchten, dass es für 'short' und' char' funktioniert, könnten Sie die Variable wahrscheinlich auf 'char' umsetzen, bevor Sie sie verwenden. Das sollte die meisten Überlaufprobleme behandeln. Ich denke ... –

5
#define ISVARSIGNED(V) ((-(V) < 0) != ((V) < 0)) 

Ohne den Wert der Variablen zu zerstören. Aber funktioniert nicht für 0 Werte.

Was:

#define ISVARSIGNED(V) (((V)-(V)-1) < 0) 
+3

#define ISVARSIGNED (V) ((-V <0)! = (V <0)) – plinth

+0

yeah Ich hätte nicht C & Pd das überflüssige Zeug ;-) – ypnos

+0

@plinth, du hast das "Extra" -Parens um 'V' vergessen, was es vor wahnsinnigen Makroerweiterungen sicher macht – rmeador

5

Wenn Sie GCC verwenden können Sie die typeof Schlüsselwort nicht um den Wert zu überschreiben:

#define ISVARSIGNED(V) ({ typeof (V) _V = -1; _V < 0 ? 1 : 0 }) 

Diese eine temporäre Variable erstellt, _V, dass die hat gleicher Typ wie V.

Wie für die Portabilität, weiß ich nicht. Es wird auf einem Kompliment-Computer von zwei funktionieren (a.k.a., alles, was dein Code jemals wahrscheinlich weiterlaufen wird), und ich glaube, dass es auf eigenen Kompliment- und Zeichen-und-Betrag-Maschinen auch arbeiten wird. Als Randnotiz möchten Sie, wenn Sie typeof verwenden, -1 in typeof (V) umwandeln, um es sicherer zu machen (d. H. Weniger wahrscheinlich, Warnungen auszulösen).

+0

In C++ funktioniert der Standard unabhängig von der Ganzzahldarstellung (für n Bits ist der Wert 2^n - 1). Ich habe nicht den C-Standard (beide). –

+0

Ich auch nicht, aber ich erinnere mich an Wikipedia lesen (die Quelle aller Dinge wahr: P), dass der C-Standard erlaubt die drei Darstellungen, die ich aufgeführt. Nicht dass jemand sie mehr benutzt ... –

-1

Ein Unterscheidungsmerkmal von vorzeichenbehafteten/vorzeichenlosen Berechnungen ist, dass das höchstwertige Bit kopiert wird, wenn Sie eine vorzeichenbehaftete Zahl nach rechts verschieben. Wenn Sie eine Zahl ohne Vorzeichen verschieben, sind die neuen Bits 0.

#define HIGH_BIT(n) ((n) & (1 << sizeof(n) * CHAR_BITS - 1)) 
#define IS_SIGNED(n) (HIGH_BIT(n) ? HIGH_BIT(n >> 1) != 0 : HIGH_BIT(~n >> 1) != 0 

Also im Grunde das Makro einen bedingten Ausdruck verwendet, um zu bestimmen, ob das hohe Bit einer Zahl festgelegt ist. Wenn dies nicht der Fall ist, setzt das Makro es, indem es die Zahl bitweise negiert. Wir können keine arithmetische Negation machen, weil -0 == 0. Dann verschieben wir uns um 1 Bit nach rechts und testen, ob eine Zeichenerweiterung aufgetreten ist.

Dies setzt 2-Komplement-Arithmetik voraus, aber das ist normalerweise eine sichere Annahme.

+0

Haben Sie eine Quelle für diese Annahmen über das Verhalten von Bitverschiebungen? –

+0

Der C99-Standard (Abschnitt 6.5.7) besagt, dass eine Rechtsverschiebung eines vorzeichenbehafteten negativen Werts implementiert ist. Meine Interpretation ist, dass es eine Zeichenerweiterung auf einer 2er-Komplement-Maschine geben wird. Da C nicht für Zweierkomplementarchitekturen spezifisch ist, werden sie nicht herauskommen und dies sagen. –

+0

Sie würden mehr PC sein, indem Sie 'CHAR_BITS' statt der magischen' 8' verwenden. (Ja, ich weiß, es gibt nur sehr wenige von uns, die nicht an Maschinen arbeiten, bei denen ein Byte 8 Bit beträgt. Immer noch.) – sbi

1

Diese einfache Lösung hat keine Nebenwirkungen, einschließlich des Vorteils, nur einmal auf v zu verweisen (was in einem Makro wichtig ist). Wir verwenden die gcc Erweiterung „typeof“ die Art von v zu bekommen, und dann werfen -1 auf diese Art:

#define IS_SIGNED_TYPE(v) ((typeof(v))-1 <= 0) 

Es ist < = anstatt nur < Compiler-Warnungen für einige Fälle zu vermeiden (wenn aktiviert).

0

Warum in aller Welt brauchen Sie ein Makro?Vorlagen sind dafür geeignet:

template <typename T> 
bool is_signed(T) { 
    static_assert(std::numeric_limits<T>::is_specialized, "Specialize std::numeric_limits<T>"); 
    return std::numeric_limits<T>::is_signed; 
} 

Welche funktioniert out-of-the-Box für alle grundlegenden Integraltypen. Es wird auch bei der Kompilierung auf Zeigern fehlschlagen, die die Version, die nur Subtraktion und Vergleich verwendet, wahrscheinlich nicht.

EDIT: Oops, erfordert immer noch die Frage C, Vorlagen die nette Art sind: P

0

Ein anderer Ansatz für alle "machen negativ" Antwort:

#define ISVARSIGNED(V) (~(V^V)<0) 

Auf diese Weise Es gibt keine Notwendigkeit, Sonderfälle für verschiedene Werte von V zu haben, da V V, VV = 0 ist.

Verwandte Themen