Warum sollten Sie den ternären Operator verwenden, ohne einen Wert für die “wahre” Bedingung zuzuweisen (x = x ?: 1)

Lesezeit: 5 Minuten

Benutzeravatar von RickNotFred
RickNotFred

Im Android-Open-Source-Qemu-Code bin ich auf diese Codezeile gestoßen:

machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */

Ist das nur eine verwirrende Art zu sagen:

if (machine->max_cpus) {
   ; //do nothing
} else {
 machine->max_cpus = 1;
}

Wenn ja, wäre es nicht klarer als:

if (machine->max_cpus == 0) machine->max_cpus = 1;

Interessanterweise kompiliert und funktioniert dies gut mit gcc, kompiliert aber nicht weiter http://www.comeaucomputing.com/tryitout/ .

  • Warte was?! … das sieht für mich nach einem Bug aus.

    – Konrad Rudolf

    10. Mai 2010 um 20:36 Uhr

  • @ Konrad – wahrscheinlich nicht. Der Kommentar schlägt vor, dass die Zeile einen Standardwert festlegt – wenn “max_cpus nicht festgelegt ist, den Standardwert festlegen”.

    – Mac

    10. Mai 2010 um 20:40 Uhr


  • Wenn es legal ist, würde ich es vermeiden, weil es zu sehr nach einem Fehler aussieht. Hier haben wir eine große Diskussion darüber, was beweist, dass es klarer gewesen wäre, die if-Anweisung zu verwenden.

    – Jim Tshr

    10. Mai 2010 um 21:05 Uhr

  • @ Konrad: Nö. In einer großen Compiler-spezifischen Codebasis werden Sie kaum jede einzelne Verwendung einer Erweiterung mit „das ist eine Erweiterung“ kommentieren. Zugegeben, dies ist für den Leser über die Google-Suche besonders schwierig zu finden, verglichen mit beispielsweise __builtin_clzoder (in C++) long long

    – Steve Jessop

    10. Mai 2010 um 22:42 Uhr


  • “wäre es nicht klarer als”. Ja. Aber vielleicht nicht so sehr, wenn es so gewesen wäre machine->max_cpus = arguments->max_cpus ?: 1;. Könnte das Ergebnis der übermäßigen Verwendung eines Idioms sein, das in einigen Fällen gut funktioniert, in anderen jedoch nicht.

    – Steve Jessop

    10. Mai 2010 um 22:48 Uhr


Benutzeravatar von Uri
Uri

Das ist gestattet in GNU als obskure Erweiterung von C

5.7 Bedingungen mit weggelassenen Operanden

Der mittlere Operand in einem Bedingungsausdruck kann weggelassen werden. Wenn der erste Operand dann nicht Null ist, ist sein Wert der Wert des bedingten Ausdrucks.

Daher der Ausdruck

 x ? : y

hat den Wert von x, wenn dieser nicht Null ist; andernfalls der Wert von y.

Dieses Beispiel ist vollkommen äquivalent zu

 x ? x : y

In diesem einfachen Fall ist die Möglichkeit, den mittleren Operanden wegzulassen, nicht besonders nützlich. Nützlich wird es, wenn der erste Operand einen Nebeneffekt enthält oder (wenn es sich um ein Makroargument handelt) enthalten kann. Dann würde die Wiederholung des Operanden in der Mitte den Nebeneffekt zweimal ausführen. Das Weglassen des mittleren Operanden verwendet den bereits berechneten Wert ohne die unerwünschten Auswirkungen einer Neuberechnung.

Wie Sie wahrscheinlich erraten können, wird aus Gründen der Lesbarkeit und Portabilität empfohlen, dies zu vermeiden. Ich bin ehrlich überrascht, eine solche Grammatik-inkompatible Erweiterung für C zu sehen.

  • Interessant. Dies scheint als syntaktisches Merkmal sowohl unglaublich nützlich als auch unglaublich schwer zu verstehen zu sein.

    – Ben Zotto

    10. Mai 2010 um 20:44 Uhr

  • interessant. das wurde ?? in C# “x wenn x nicht null ist, sonst y” => c ?? j

    – Stefan

    10. Mai 2010 um 21:01 Uhr

  • @ Potatoswatter: nicht wirklich, da vermutlich max_cpus ist kein boolescher Wert. || hat keine Möglichkeit der Auswertung 3 wenn der vorherige Wert war 3.

    – Steve Jessop

    10. Mai 2010 um 22:44 Uhr


  • Da sich dieses Idiom anders verhält als a ternary conditional und eher wie ein logical or in den meisten Sprachen, schlage ich vor, heißt es fortan das ternarator

    – aelgoa

    11. Februar 2013 um 8:28 Uhr

  • Ich möchte hinzufügen, dass clang dies auch unterstützt (wie bei vielen GNU-Erweiterungen), es sei denn, es wird mit -pedantic ausgeführt.

    – Tor Klingeberg

    21. August 2014 um 15:45 Uhr

Das ist ein GCC-Erweiterung das bedeutet “wenn die Bedingung wahr ist, verwende sie, sonst verwende diesen anderen Wert”, also

machine->max_cpus = machine->max_cpus ?: 1;

ist eine Abkürzung für

machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;

Wenn die Bedingung jedoch Nebenwirkungen hat, wird sie nur einmal ausgeführt

Unter Verwendung des -pedantic-Flags von gcc heißt es

foo.c:5: Warnung: ISO C verbietet das Weglassen des mittleren Begriffs eines ?:-Ausdrucks

Es ist ein GCC-Erweiterungund es wird interessanter und nützlicher, wenn die Erkrankung Nebenwirkungen hat.

In diesem Fall, ja, ich für meinen Teil würde zustimmen, dass es mehr als alles andere obskur ist.

Die K&R BNF zeigt an, dass ein Ausdruck zwischen “?” und “:”. Ich denke nicht, dass gcc das ohne Diagnose kompilieren sollte.

  • Ich glaube, es ist eine nicht standardmäßige gcc-Erweiterung. Am besten vermieden.

    – PaulR

    10. Mai 2010 um 20:41 Uhr

Es gibt noch einen anderen nützlichen Fall dafür – die Eliminierung von Zwischenvariablen beim Aufruf einer Funktion oder Methode, die möglicherweise nil zurückgibt, die wir vermeiden möchten, zweimal aufzurufen. Angenommen, wir möchten beispielsweise (Objective-C) eine Datei in ein Array entpacken, falls vorhanden, andernfalls ein leeres Array zurückgeben.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        backlog = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData] ?: backlog;
    }
    return backlog;
}

Die Alternativen sind weniger prägnant.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            backlog = tempArray;
        }
    }
    return backlog;
}

Oder hässlicher mit mehreren Rücksendungen usw.

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            return tempArray;
        }
    }
    return @[];
}

Es ist also nützlicher syntaktischer Zucker, den ich ziemlich lesbar finde. Die Nachteile sind

  • Implizite Konvertierung eines Zeigers in einen bool. Dies ist eine seit langem bestehende C-Konvention, aber die meisten modernen Sprachen erlauben dies nicht, was jegliche Portierungsbemühungen erschwert.

  • Wie andere gesagt haben, ist es auch eine Nicht-Standard-Erweiterung, daher sollte es vermieden werden, wenn Portabilität überhaupt in Betracht gezogen wird.

  • Ich glaube, es ist eine nicht standardmäßige gcc-Erweiterung. Am besten vermieden.

    – PaulR

    10. Mai 2010 um 20:41 Uhr

Benutzeravatar von DarkTrick
Dunkler Trick

Ich habe das Gefühl, dass die anderen Antworten die Frage im Titel nicht beantworten und auch das Tag nehmen c berücksichtigen. Deshalb füge ich eine weitere Antwort hinzu.

Ich verwende diese Syntax, um zu verhindern, dass mein Code durch if-Anweisungen hässlich wird.

foo(1) == TRUE ?: error_quit("foo(1) failed");
foo(2) == TRUE ?: error_quit("foo(2) failed");
foo(3) == TRUE ?: error_quit("foo(3) failed");
foo(4) == TRUE ?: error_quit("foo(4) failed");

Den eigentlichen Funktionsaufruf sehen Sie direkt am Anfang der Zeile. Verglichen mit den Versionen unten, wo die führende if versperrt die direkte Sicht auf den Funktionsaufruf.

if (foo(1) == FALSE) error_quit("foo(1)" failed");
if (foo(2) == FALSE) error_quit("foo(2)" failed");
if (foo(3) == FALSE) error_quit("foo(3)" failed");    
if (foo(4) == FALSE) error_quit("foo(4)" failed");

oder noch schwerer zu lesen:

if (foo(1) == FALSE){
  error_quit("foo(1)" failed");
}

if (foo(2) == FALSE){
  error_quit("foo(2)" failed");
}

if (foo(3) == FALSE){
  error_quit("foo(3)" failed");
}

if (foo(4) == FALSE){
  error_quit("foo(4)" failed");
}

1402080cookie-checkWarum sollten Sie den ternären Operator verwenden, ohne einen Wert für die “wahre” Bedingung zuzuweisen (x = x ?: 1)

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

Privacy policy