Was macht ein && Operator, wenn es in C keine linke Seite gibt?

Lesezeit: 7 Minuten

Joshuas Benutzeravatar
Josua

Ich habe ein Programm in C gesehen, das Code wie den folgenden hatte:

static void *arr[1]  = {&& varOne,&& varTwo,&& varThree};

varOne: printf("One") ;
varTwo: printf("Two") ;
varThree: printf("Three") ;

Ich bin verwirrt darüber, was die && tut, weil links davon nichts ist. Wird es standardmäßig als null ausgewertet? Oder ist das ein Sonderfall?

Bearbeiten: Weitere Informationen hinzugefügt, um die Frage / den Code für meine Frage klarer zu machen. Vielen Dank für die Hilfe. Dies war ein Fall der gcc-spezifischen Erweiterung.

  • Das ist keiner && aber zwei &. Und ungültig in C, als Ergebnis von & ist kein gültiger Ausdruck für einen anderen &. Sicher, dass das kein C++-Code ist?

    – zu ehrlich für diese Seite

    6. September 2016 um 21:12 Uhr


  • Das sieht ein bisschen wie GCC aus berechnete gotos. Gibt varOne, varTwound varThree Etiketten?

    – Benutzer2357112

    6. September 2016 um 21:12 Uhr

  • @ user2357112: Guter Punkt. Auch hier hätte ein reproduzierbares Minimalbeispiel geholfen.

    – zu ehrlich für diese Seite

    6. September 2016 um 21:14 Uhr

  • @Olaf: Nein, weil && wird vom Lexer immer als eine Einheit behandelt. Es ist der gleiche Grund, warum so etwas wie return0 wird als einzelne Kennung statt als behandelt return 0.

    – Umarmung

    6. September 2016 um 21:23 Uhr

  • Ich habe für die Wiedereröffnung gestimmt, weil mir das Schließen als “Off-Topic” hier falsch erscheint: Hier geht es sehr sicher um die berechneten Gotos von GCC, und nach einer schnellen Suche scheint es, als hätten wir keine Frage, die sich speziell damit befasst. Eine solche Frage zu haben, wäre nett und erspart möglicherweise zukünftige “das ist ein berechnetes goto” .. “was ist ein berechnetes goto”-Diskussionen. Obwohl es entsprechend gekennzeichnet sein sollte (gcc und Spracherweiterung?)

    – Daniel Jour

    6. September 2016 um 22:10 Uhr

Benutzeravatar von Keith Thompson
Keith Thompson

Es ist eine gcc-spezifische Erweiterung, eine unäre && Operator, der auf einen Label-Namen angewendet werden kann und seine Adresse als liefert void* Wert.

Im Rahmen der Erweiterung goto *ptr; wo erlaubt ist ptr ist ein Ausdruck des Typs void*.

Es ist dokumentiert hier im gcc-Handbuch.

Sie können die Adresse eines in der aktuellen Funktion (oder einer enthaltenden Funktion) definierten Labels mit dem unären Operator abrufen &&. Der Wert hat Typ void *. Dieser Wert ist eine Konstante und kann überall dort verwendet werden, wo eine Konstante dieses Typs gültig ist. Zum Beispiel:

void *ptr;
/* ... */
ptr = &&foo;

Um diese Werte zu verwenden, müssen Sie in der Lage sein, zu einem zu springen. Dies geschieht mit der berechneten goto-Anweisung, goto *exp;. Zum Beispiel,

goto *ptr;

Jeder Ausdruck des Typs void * ist erlaubt.

Wie zwol in einem Kommentar betont, verwendet gcc && eher als das Offensichtlichere & weil ein Etikett und ein Objekt mit demselben Namen gleichzeitig sichtbar sein können &foo möglicherweise mehrdeutig, wenn & bedeutet “Adresse des Etiketts”. Bezeichnungsnamen belegen ihren eigenen Namensraum (nicht im Sinne von C++) und können nur in bestimmten Kontexten erscheinen: definiert durch a beschriftete Aussageals Ziel von a goto -Anweisung oder, für gcc, als Operand von unary &&.

  • Die nächste Frage ist also, warum es so ist *arr[1][8432]

    – pm100

    7. September 2016 um 0:12 Uhr

  • @ pm100: Keine Ahnung, ohne einen Kontext zu sehen. Es ist ein Array 1 von Array 8432 von void* Zeiger. Die ersten 3 werden initialisiert; vermutlich werden dem Rest später Werte zugewiesen. Ich weiß nicht, warum es eher ein 2-d-Array als ein 1-d-Array ist (vielleicht ein Pseudo-Pass-by-Reference-Hacker). Aber das OP hat nicht danach gefragt, also …

    – Keith Thompson

    7. September 2016 um 0:15 Uhr

  • Falls Sie sich fragen, verwendet die Erweiterung &&label statt der natürlicheren &label weil Sie in derselben Funktion sowohl ein Label als auch eine Variable mit demselben Namen haben können.

    – zol

    7. September 2016 um 13:02 Uhr

Benutzeravatar von MM
MM

Dies ist eine gcc-Erweiterung, die als “Labels as Values” bekannt ist. Link zur gcc-Dokumentation.

In dieser Erweiterung && ist ein unärer Operator, der auf a angewendet werden kann Etikett. Das Ergebnis ist ein Wert vom Typ void *. Dieser Wert kann später in a dereferenziert werden goto -Anweisung, damit die Ausführung zu diesem Label springt. Außerdem ist Zeigerarithmetik für diesen Wert zulässig.

Das Label muss dieselbe Funktion haben; oder in einer einschließenden Funktion, falls der Code auch die gcc-Erweiterung von “verschachtelten Funktionen” verwendet.

Hier ist ein Beispielprogramm, in dem die Funktion zur Implementierung einer Zustandsmaschine verwendet wird:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
    void *tab[] = { &&foo, &&bar, &&qux };

    // Alternative method
    //ptrdiff_t otab[] = { &&foo - &&foo, &&bar - &&foo, &&qux - &&foo };

    int i, state = 0;

    srand(time(NULL));

    for (i = 0; i < 10; ++i)
    {
        goto *tab[state];

        //goto *(&&foo + otab[state]);

    foo:
        printf("Foo\n");
        state = 2;
        continue;
    bar:
        printf("Bar\n");
        state = 0;
        continue;
    qux:
        printf("Qux\n");
        state = rand() % 3;
        continue;
    }
}

Zusammenstellung und Ausführung:

$ gcc -o x x.c && ./x
Foo
Qux
Foo
Qux
Bar
Foo
Qux
Qux
Bar
Foo

  • Das Beispiel in der gcc-Dokumentation verwendet int als Typ für das Ergebnis der Subtraktion – nicht sicher, was passiert, wenn die Differenz den Bereich von überschreitet int. Vielleicht ist das aufgrund der Anforderung, dass beide in der gleichen Funktion sein müssen, tatsächlich nicht möglich, aber vielleicht ptrdiff_t wäre besser.

    – MM

    7. September 2016 um 0:11 Uhr

  • Der richtige Typ ist in der Tat ptrdiff_t; dies ist nur ein Fehler im Handbuch (wahrscheinlich aus der Zeit, als ptrdiff_t und int waren fast immer vom gleichen Typ). Sie können dies selbst sehen, indem Sie kompilieren void test(void) { a:; b:; __typeof (&&a - &&b) d; ptrdiff_t *pd = &d; int *qd = &d; } auf einem ABI, wo sie nicht vom gleichen Typ sind.

    – zol

    7. September 2016 um 13:19 Uhr


Mir ist kein Operator bekannt, der in C so funktioniert. Je nach Kontext kann das kaufmännische Und in C viele verschiedene Dinge bedeuten.

Address-Of-Operator

Direkt vor einem lvalue, zB

int j;
int* ptr = &j;

Im obigen Code ptr speichert die Adresse von j, & nimmt in diesem Zusammenhang die Adresse eines beliebigen lvalue. Der folgende Code wäre für mich sinnvoller gewesen, wenn er so geschrieben worden wäre.

static int varOne;
static int varTwo;
static int varThree;

static void *arr[1][8432] = { { &varOne,&varTwo, &varThree } };

Logisches UND

Der logische UND-Operator ist einfacher, im Gegensatz zum obigen Operator ist er ein binärer Operator, was bedeutet, dass er einen linken und einen rechten Operanden erfordert. Die Funktionsweise besteht darin, den linken und den rechten Operanden auszuwerten und wahr zurückzugeben, wenn beide wahr sind, oder größer als 0, wenn sie nicht bool sind.

bool flag = true;
bool flag2 = false;
if (flag && flag2) {
    // Not evaluated
}
flag2 = true;
if (flag && flag2) {
   // Evaluated
}

Bitweises UND

Eine andere Verwendung des kaufmännischen Und in C ist ein bitweises UND. Er ähnelt dem logischen UND-Operator, außer dass er nur ein kaufmännisches Und verwendet und eine UND-Operation auf Bitebene durchführt.

Nehmen wir an, wir haben eine Zahl und diese entspricht der unten gezeigten binären Darstellung. Die UND-Operation funktioniert folgendermaßen:

0 0 0 0 0 0 1 0
1 0 0 1 0 1 1 0
---------------
0 0 0 0 0 0 1 0

Im C++-Land werden die Dinge komplizierter. Das kaufmännische Und kann nach einem Typ platziert werden, um einen Referenztyp zu bezeichnen (Sie können es sich als eine weniger mächtige, aber sichere Art von Zeiger vorstellen), dann wird es noch komplizierter mit 1) r-Wert-Referenz, wenn zwei kaufmännische Und-Zeichen dahinter platziert werden eine Art. 2) Universelle Referenzen, wenn zwei kaufmännische Und-Zeichen nach einem Vorlagentyp oder automatisch abgezogenen Typ platziert werden.

Ich denke, Ihr Code wird aufgrund einer Art Erweiterung wahrscheinlich nur in Ihrem Compiler kompiliert. Daran dachte ich https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C aber ich bezweifle, dass das so ist.

  • Es ist eine gcc-Erweiterung. Siehe die anderen Antworten.

    – Keith Thompson

    7. September 2016 um 0:08 Uhr

  • Dies ist eine korrekte Information, die jedoch leider nicht mit der Frage zusammenhängt

    – MM

    7. September 2016 um 0:09 Uhr

  • Autsch, Meta-Effekt ist schlecht geworden. Diese Antwort ist (oder besser war) gut, bevor sie geschlossen und wieder geöffnet wurde und sich in der Zwischenzeit geändert hat.

    – Daniel Jour

    7. September 2016 um 15:20 Uhr

  • @KeithThompson Mir war die Erweiterung nicht besonders bewusst, aber ich habe erwähnt, dass es sich wahrscheinlich um eine Erweiterung handelt, auf jeden Fall danke für die Info 🙂

    – Fred Garnier

    7. September 2016 um 17:56 Uhr

  • @Daniel Wurde diese Frage auf Meta diskutiert? Ich konnte es nicht finden. Es ist ein bisschen unglücklich, dass die Frage so geändert wurde, dass diese Antwort ungültig wird.

    – Didier L

    13. September 2016 um 22:24 Uhr

1409420cookie-checkWas macht ein && Operator, wenn es in C keine linke Seite gibt?

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

Privacy policy