Ich verwende ein SDK für ein eingebettetes Projekt. In diesem Quellcode habe ich einen Code gefunden, den ich zumindest eigenartig fand. An vielen Stellen im SDK gibt es Quellcode in diesem Format:
#define ATCI_IS_LOWER( alpha_char ) ( ( (alpha_char >= ATCI_char_a) && (alpha_char <= ATCI_char_z) ) ? 1 : 0 )
#define ATCI_IS_UPPER( alpha_char ) ( ( (alpha_char >= ATCI_CHAR_A) && (alpha_char <= ATCI_CHAR_Z) ) ? 1 : 0 )
Macht die Verwendung des ternären Operators hier einen Unterschied?
Ist nicht
#define FOO (1 > 0)
das Gleiche wie
#define BAR ( (1 > 0) ? 1 : 0)
?
Ich habe versucht, es mit zu bewerten
printf("%d", FOO == BAR);
und erhalten Sie das Ergebnis 1, so scheint es, dass sie gleich sind. Gibt es einen Grund, den Code so zu schreiben, wie sie es getan haben?
c++
c
boolean
ternary-operator
Viktor S.
quelle
quelle
(alpha_char)
stattdessen verwendet werdenalpha_char
, nur um sicherzustellen, dass es nicht kaputt geht, wenn jemand etwas Verrücktes versuchtATCI_IS_LOWER(true || -1)
.boolean
Typ hatte, also verschwendete ich unzählige Zeit damit, die Schreckenif (n)
zu ändern ,if (0 != n)
und fügte wahrscheinlich eine zweifelhafte Besetzung hinzu, "um sicherzugehen". Ich bin mir sicher, dass ich auch kugelsichere Ungleichungen magif (a < b) ...
. Sicher , es sah aus wie Pascalif a < b then ...
, aber ich wusste , dass Cs<
war keine ,boolean
sondern einint
, und eineint
könnte sein , fast alles ! Angst führt zu Vergoldung, Vergoldung führt zu Paranoia, Paranoia führt zu ... Code wie diesem.Antworten:
Sie haben Recht, in C ist es tautolog. Sowohl Ihre besondere ternäre Bedingung als auch
(1 > 0)
sind vom Typint
.Aber es wäre in C ++ Rolle , obwohl in einigen neugierigen Ecke Fällen (zB als Parameter überladene Funktionen), da Ihr dreifach konditionaler Ausdruck vom Typ ist
int
, während(1 > 0)
vom Typbool
.Ich vermute, dass der Autor einige Überlegungen angestellt hat, um die C ++ - Kompatibilität zu erhalten.
quelle
bool <-> int
Konvertierungen sind in C ++ gemäß §4.7 / 4 aus dem Standard (integrale Konvertierung) impliziert. Wie wäre es also wichtig?foo
, von denen eine eine undconst bool&
die andere eine übernimmtconst int&
. Einer von ihnen bezahlt Sie, der andere formatiert Ihre Festplatte neu. In diesem Fall möchten Sie möglicherweise sicherstellen, dass Sie die richtige Überlastung aufrufen.int
anstatt das Ternäre zu verwenden?Es gibt Flusenwerkzeuge, die der Meinung sind, dass das Ergebnis eines Vergleichs boolesch ist und nicht direkt in der Arithmetik verwendet werden kann.
Keine Namen zu nennen oder mit den Fingern zu zeigen, aber PC-Lint ist ein solches Flusenwerkzeug .
Ich sage nicht, dass sie Recht haben, aber es ist eine mögliche Erklärung dafür, warum der Code so geschrieben wurde.
quelle
Not to name names or point any fingers,
aber du hast beides getan, lol.Sie werden dies manchmal in sehr altem Code sehen, bevor es einen C-Standard gab,
(x > y)
der mit der Ziffer 1 oder 0 bewertet wurde. Einige CPUs würden dies lieber auf -1 oder 0 auswerten, und einige sehr alte Compiler sind möglicherweise gerade gefolgt, sodass einige Programmierer der Meinung waren, dass sie die zusätzliche Abwehr benötigen.Sie werden dies manchmal auch sehen, weil ähnliche Ausdrücke nicht unbedingt als numerisch 1 oder 0 ausgewertet werden. Zum Beispiel in
#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)
Der innere
&
Ausdruck ergibt entweder 0 oder den numerischen Wert vonF_DO_GRENFELZ
, der wahrscheinlich nicht 1 ist, und? 1 : 0
dient daher zur Kanonisierung. Ich persönlich denke, es ist klarer, das als zu schreiben#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)
aber vernünftige Leute können nicht zustimmen. Wenn Sie eine ganze Reihe von Ausdrücken hintereinander hatten und verschiedene Arten von Ausdrücken testeten, hätte jemand vielleicht entschieden, dass es besser zu warten ist, alle zu
? 1 : 0
beenden , als sich Gedanken darüber zu machen, welche tatsächlich benötigt werden.quelle
!!( expr )
Kanonisierung eines Booleschen Werts, aber ich gebe zu, dass es verwirrend ist, wenn Sie nicht damit vertraut sind.Es gibt einen Fehler im SDK-Code, und der Ternär war wahrscheinlich ein Problem, um ihn zu beheben.
Als Makro können die Argumente (alpha_char) ein beliebiger Ausdruck sein und sollten in Klammern gesetzt werden, da Ausdrücke wie 'A' && 'c' den Test nicht bestehen.
#define IS_LOWER( x ) ( ( (x >= 'a') && (x <= 'z') ) ? 1 : 0 ) std::cout << IS_LOWER('A' && 'c'); **1** std::cout << IS_LOWER('c' && 'A'); **0**
Aus diesem Grund sollten Makroargumente in der Erweiterung immer in Klammern gesetzt werden.
In Ihrem Beispiel (aber mit Parametern) sind beide fehlerhaft.
#define FOO(x) (x > 0) #define BAR(x) ((x > 0) ? 1 : 0)
Sie würden am korrektesten durch ersetzt
#define BIM(x) ((x) > 0)
@CiaPan In dem folgenden Kommentar wird besonders darauf hingewiesen, dass die mehrmalige Verwendung eines Parameters zu undefinierbaren Ergebnissen führt. Zum Beispiel
#define IS_LOWER( x ) (((x) >= 'a') && ((x) <= 'z')) char ch = 'y'; std::cout << IS_LOWER(ch++); **1** **BUT ch is now '{'**
quelle
IS_LOWER(++ var)
kann erhöhtvar
ein- oder zweimal, zusätzlich kann es nicht zur Kenntnis und erkennt Klein'z'
wennvar
war'y'
vor dem Makroaufruf. Deshalb sollten solche Makros vermieden werden oder das Argument einfach an eine Funktion weiterleiten.In C spielt es keine Rolle. Boolesche Ausdrücke in C haben einen Typ
int
und einen Wert, der entweder0
oder1
so istConditionalExpr ? 1 : 0
hat keine Wirkung.
In C ++ ist es effektiv eine Umwandlung in
int
, da bedingte Ausdrücke in C ++ vom Typ sindbool
.#include <stdio.h> #include <stdbool.h> #ifndef __cplusplus #define print_type(X) _Generic(X, int: puts("int"), bool: puts("bool") ); #else template<class T> int print_type(T const& x); template<> int print_type<>(int const& x) { return puts("int"); } template<> int print_type<>(bool const& x) { return puts("bool"); } #endif int main() { print_type(1); print_type(1 > 0); print_type(1 > 0 ? 1 : 0); /*c++ output: int int int cc output: int bool int */ }
Es ist auch möglich, dass kein Effekt beabsichtigt war, und der Autor dachte einfach, dass dies den Code klarer macht.
quelle
_Bool
, jetzt wo C_Bool
und hat_Generic
. Es sollte nicht viel Code brechen, da sich alle kleineren Typenint
in den meisten Kontexten sowieso automatisch weiterentwickeln .Eine einfache Erklärung ist, dass einige Leute entweder nicht verstehen, dass eine Bedingung den gleichen Wert in C zurückgeben würde, oder sie denken, dass das Schreiben sauberer ist
((a>b)?1:0)
.Das erklärt, warum einige ähnliche Konstrukte auch in Sprachen mit richtigen Booleschen Werten verwenden, was in der C-Syntax der Fall wäre
(a>b)?true:false)
.Dies erklärt auch, warum Sie dieses Makro nicht unnötig ändern sollten.
quelle
Vielleicht würde eine eingebettete Software einige Hinweise geben. Möglicherweise sind viele Makros mit diesem Stil geschrieben, um leicht darauf hinzuweisen, dass ACTI-Zeilen eher direkte Logik als invertierte Logik verwenden.
quelle