Warum wird der ternäre Operator verwendet, um 1 und 0 in einem Makro zu definieren?

Lesezeit: 8 Minuten

Benutzeravatar von Viktor S
Viktor S

Ich verwende ein SDK für ein eingebettetes Projekt. In diesem Quellcode habe ich Code gefunden, den ich zumindest merkwürdig 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 auszuwerten

printf("%d", FOO == BAR);

und erhalten das Ergebnis 1, also scheinen sie gleich zu sein. Gibt es einen Grund, den Code so zu schreiben, wie sie es getan haben?

  • Nein, es gibt keinen Grund. Sie haben Recht.

    – Kunst

    31. März 2017 um 11:08 Uhr

  • Teilweise Off-Topic: Wann hört der Wahnsinn der Nutzung des Präprozessors auf? Hier liegt eine potentielle Mehrfachauswertung der beteiligten Funktionen vor. Einfach unnötig.

    – Stefan

    31. März 2017 um 13:29 Uhr

  • Manchmal ist es auch schön, explizit zu sein. Der ternäre Operator hier macht auf einen Blick deutlich, dass der Zweck des Makros darin besteht, einen booleschen Wert zurückzugeben.

    – Rohr

    31. März 2017 um 16:00 Uhr


  • Zumindest sollten die Makros verwendet werden (alpha_char) Anstatt von alpha_charnur damit es nicht kaputt geht wenn jemand etwas verrücktes versucht ATCI_IS_LOWER(true || -1).

    – Justin Time – Wiedereinsetzung von Monica

    1. April 2017 um 21:27 Uhr


  • Sieht aus wie die Art von CI, die vor langer Zeit geschrieben wurde. Ich war von Pascal zu C gekommen, der eine dedizierte hatte boolean Typ, also verschwendete ich unzählige Zeit damit, Horror zu ändern if (n) zu if (0 != n), wahrscheinlich fügt er eine zweifelhafte Besetzung hinzu, “um sicherzugehen”. Ich bin mir sicher, dass ich kugelsichere Ungleichungen mag if (a < b) ..., zu. Klar sah wie bei Pascal if a < b then ...aber das wusste ich Cs < war kein boolean aber ein intund ein int könnte sein fast alles! Angst führt zu Vergoldung, Vergoldung führt zu Paranoia, Paranoia führt zu… solchem ​​Code.

    – Kevin J. Chase

    1. April 2017 um 22:06 Uhr

Benutzeravatar von Bathsheba
Bathseba

Sie haben Recht, in C ist es tautolog. Sowohl Ihre besondere ternäre Bedingung und (1 > 0) sind vom Typ int.

Aber es möchten in C++ jedoch in einigen merkwürdigen Eckfällen (z. B. als Parameter für überladene Funktionen) von Bedeutung, da Ihr ternärer bedingter Ausdruck vom Typ ist intwohingegen (1 > 0) ist vom Typ bool.

Ich vermute, dass der Autor sich darüber Gedanken gemacht hat, um die C++-Kompatibilität zu erhalten.

  • ich dachte bool <-> int Konvertierungen sind in C++ durch §4.7/4 aus dem Standard implizit (ganzzahlige Konvertierung), also wie würde es ausfallen?

    – Motun

    31. März 2017 um 11:22 Uhr

  • Betrachten Sie zwei Überladungen einer Funktion fooman nimmt a const bool& der andere nimmt a const int&. Einer von ihnen bezahlt Sie, der andere formatiert Ihre Festplatte neu. Möglicherweise möchten Sie sicherstellen, dass Sie in diesem Fall die richtige Überladung aufrufen.

    – Bathseba

    31. März 2017 um 11:23 Uhr

  • wäre es nicht offensichtlicher, diesen Fall zu behandeln, indem Sie das Ergebnis in umwandeln int anstatt die ternäre zu verwenden?

    – Martinkunew

    31. März 2017 um 13:39 Uhr

  • @Bathsheba Obwohl es sich um einen legitimen Eckfall handelt, ist jeder Programmierer, der integrale Überladungen verwendet, um ein solches inkonsistentes Verhalten zu implementieren, völlig böse.

    – JAB

    31. März 2017 um 13:53 Uhr


  • @JAB: Sie müssen nicht böse sein, Sie müssen nur den (häufigen) Fehler machen, einen Code zu schreiben, der versehentlich zwei verschiedene Dinge tut (oder schlimmer noch, aufrufen undefiniertes Verhalten) abhängig vom Integraltyp und haben das Unglück, dies an einer Stelle zu tun, die radikal unterschiedliche Codepfade auslösen kann.

    Benutzer1084944

    1. April 2017 um 5:39 Uhr


Benutzeravatar von unwind
entspannen

Es gibt Linting-Tools, 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 dem Finger zu zeigen, aber PC-Lint ist so ein Fusselwerkzeug.

Ich sage nicht, dass sie Recht haben, aber es ist eine mögliche Erklärung dafür, warum der Code so geschrieben wurde.

  • Not to name names or point any fingers, aber du hast irgendwie beides gemacht, lol.

    – Stapelüberlauf

    31. März 2017 um 14:41 Uhr

Benutzeravatar von twol
zwöl

Sie werden dies manchmal in sehen sehr alt Code, bevor es einen C-Standard gab, um das zu buchstabieren (x > y) wird als Zahl 1 oder 0 ausgewertet; Einige CPUs würden dies lieber auf -1 oder 0 auswerten lassen, und einige sehr alt Compiler sind vielleicht einfach mitgekommen, daher hatten einige Programmierer das Gefühl, dass sie die zusätzliche Abwehr benötigen.

Sie werden dies manchmal auch sehen, weil ähnlich Ausdrücke nicht unbedingt als Zahl 1 oder 0 auswerten. Zum Beispiel in

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) ? 1 : 0)

das Innere &-Ausdruck ergibt entweder 0 oder den numerischen Wert von F_DO_GRENFELZwas wahrscheinlich ist nicht 1, also die ? 1 : 0 dient der Kanonisierung. Ich persönlich denke, es ist klarer, das als zu schreiben

#define GRENFELZ_P(flags) (((flags) & F_DO_GRENFELZ) != 0)

aber vernünftige Menschen können anderer Meinung sein. Wenn Sie eine ganze Reihe davon hintereinander hätten und verschiedene Arten von Ausdrücken testen würden, hätte jemand vielleicht entschieden, dass es pflegeleichter ist, sie zu setzen ? 1 : 0 am Ende von alle von ihnen, als sich Gedanken darüber zu machen, welche es tatsächlich brauchten.

  • Im Großen und Ganzen verwende ich lieber !!( expr ) um einen booleschen Wert zu kanonisieren, aber ich gebe zu, dass es verwirrend ist, wenn Sie nicht damit vertraut sind.

    – PJTrail

    5. April 2017 um 17:41 Uhr


  • @PJTraill Jedes Mal, wenn Sie Leerzeichen in Ihre Klammern einfügen, tötet Gott ein Kätzchen. Bitte. Denken Sie an die Kätzchen.

    – zol

    6. April 2017 um 16:19 Uhr


  • Das ist der beste Grund, warum ich gehört habe, in einem C-Programm keine Leerzeichen in Klammern zu setzen.

    – PJTrail

    6. April 2017 um 21:57 Uhr

Benutzeravatar von Konchog
Konchog

Es gibt einen Fehler im SDK-Code, und der Dreier war wahrscheinlich ein Knüppel, um ihn zu beheben.

Da es sich um ein Makro handelt, können die Argumente (alpha_char) beliebige Ausdrücke 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**

Deshalb sollte man Makroargumente in der Erweiterung immer in Klammern setzen.

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 besten durch ersetzt werden

#define BIM(x) ((x) > 0)

@CiaPan macht einen wichtigen Punkt im folgenden Kommentar, nämlich dass die mehr als einmalige 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 '{'**

Benutzeravatar von PSkocik
PSkocik

In C spielt es keine Rolle. Boolesche Ausdrücke in C haben Typ int und ein Wert, der entweder ist 0 oder 1Also

ConditionalExpr ? 1 : 0

hat keine Wirkung.

In C++ ist es praktisch eine Umwandlung in intda bedingte Ausdrücke in C++ einen Typ haben bool.

#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 einfach dachte, dass es den Code klarer macht.

  • Übrigens, ich denke, C sollte der Suite folgen und boolesche Ausdrücke machen _Booljetzt, wo C hat _Bool und _Generic. Es sollte nicht viel Code brechen, da alle kleineren Typen automatisch hochgestuft werden int jedenfalls in den meisten Zusammenhängen.

    – PSkočik

    31. März 2017 um 11:44 Uhr


Eine einfache Erklärung ist, dass einige Leute entweder nicht verstehen, dass eine Bedingung denselben Wert in C zurückgeben würde, oder dass sie denken, dass es sauberer zu schreiben ist ((a>b)?1:0).

Das erklärt, warum einige ähnliche Konstrukte auch in Sprachen mit richtigen Booleschen Werten verwenden, was in C-Syntax der Fall wäre (a>b)?true:false).

Dies erklärt auch, warum Sie dieses Makro nicht unnötig ändern sollten.

  • Übrigens, ich denke, C sollte der Suite folgen und boolesche Ausdrücke machen _Booljetzt, wo C hat _Bool und _Generic. Es sollte nicht viel Code brechen, da alle kleineren Typen automatisch hochgestuft werden int jedenfalls in den meisten Zusammenhängen.

    – PSkočik

    31. März 2017 um 11:44 Uhr


Benutzeravatar von J.Guarin
J. Guarin

Vielleicht würde es als eingebettete Software einige Hinweise geben. Vielleicht gibt es viele Makros, die in diesem Stil geschrieben wurden, um leicht darauf hinzuweisen, dass ACTI-Zeilen direkte Logik anstelle von invertierter Logik verwenden.

1418160cookie-checkWarum wird der ternäre Operator verwendet, um 1 und 0 in einem Makro zu definieren?

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

Privacy policy