2016-09-06 1 views
50

sah ich ein Programm in C, den Code wie folgt hatte:Was macht ein && Operator, wenn in C keine linke Seite ist?

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

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

Ich bin verwirrt, was die && tut, weil es nichts links von ihm ist. Wird es standardmäßig als null bewertet? Oder ist das ein Sonderfall?

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

+11

Das ist nicht ein '&&' aber zwei '&'. Und ungültig in C, weil das Ergebnis von '&' kein gültiger Ausdruck für ein anderes '&' ist. Sicher, das ist kein C++ Code ?? – Olaf

+34

Das sieht irgendwie aus wie GCC's [computed gotos] (https://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Labels-as-Values.html). Sind die Bezeichnungen "varOne", "varTwo" und "varThree"? – user2357112

+12

@ user2357112: Guter Punkt. Wieder einmal hätte ein [McVE] geholfen. – Olaf

Antwort

60

Es ist eine gcc-spezifische Erweiterung, ein unärer &&-Operator, der auf einen Labelnamen angewendet werden kann, der seine Adresse als void*-Wert angibt.

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

Es ist dokumentiert here in der GCC-Handbuch.

Sie können die Adresse eines in der aktuellen Funktion (oder eine Funktion enthält) mit dem unären Operator && definiert Etikett. 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 zu einem springen können. 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 weist darauf hin, gcc verwendet && anstatt die offensichtlichen &, weil ein Etikett und ein Objekt mit dem gleichen Namen gleichzeitig sichtbar sein können, so dass &foo möglicherweise nicht eindeutig, wenn & bedeutet „Adresse des Labels“ . Beschriftungsnamen belegen ihren eigenen Namespace (nicht im C++ Sinne) und können nur in bestimmten Kontexten auftreten: definiert durch eine beschriftete Anweisung als Ziel einer goto Anweisung, oder, für gcc, als Operand der unären && .

+0

so ist die nächste Frage, warum seine '* arr [1] [8432]' – pm100

+0

@ pm100: Keine Ahnung, ohne einige Kontext zu sehen. Es ist ein Array 1 von Array 8432 von 'void *' Zeigern. Die ersten 3 werden initialisiert; vermutlich haben die anderen ihnen später Werte zugewiesen. Ich weiß nicht, warum es ein 2-d-Array ist und nicht ein 1-d-Array (vielleicht ein Pseudo-Pass-by-Reference-Hacker). Aber das OP hat nicht danach gefragt, also ... –

+4

Falls Sie sich wundern, verwendet die Erweiterung '&& label' anstelle des natürlicheren' & label', weil Sie sowohl ein Label als auch eine Variable mit demselben Namen haben können in der gleichen Funktion. – zwol

-6

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

Adressoperator

Kurz vor einem L-Wert, z.B.

int j; 
int* ptr = &j; 

In dem obigen Code, ptr speichert die Adresse j, & in diesem Zusammenhang ist unter der Adresse jedes lvalue. Der Code unten hätte mir mehr Sinn gemacht, wenn er so geschrieben wäre.

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

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

Logisches UND

Die logische UND-Operator ist einfacher, im Gegensatz zu den Betreiber über, es ist ein binärer Operator, dh es einen linken und rechten Operanden erfordert. Die Art, wie es funktioniert, besteht darin, den linken und 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 
} 

bitweise UND

Eine weitere Verwendung der Et-Zeichen in C, führt eine bitweise AND. Es ist ähnlich wie der logische UND-Operator, außer dass es nur ein kaufmännisches Und verwendet, und führt eine UND-Operation auf der Bit-Ebene durch.

Nehmen wir an, wir haben eine Reihe haben und dass es in die binäre Darstellung abbildet unten gezeigt, die AND-Operation funktioniert wie folgt:

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

In 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 weniger leistungsfähigen aber sicheren Zeiger vorstellen), dann wird die Sache noch komplizierter mit 1) r-Wert-Referenz, wenn zwei Und-Zeichen gefolgt werden eine Art. 2) Universalreferenzen, wenn zwei Et-Zeichen nach einem Schablonentyp oder einem automatisch abgezogenen Typ platziert werden.

Ich denke, dass Ihr Code wahrscheinlich nur in Ihrem Compiler aufgrund einer Erweiterung einiger Arten kompiliert. Ich dachte an diese https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C, aber ich bezweifle, dass das der Fall ist.

+0

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

+12

Dies ist die richtige Information, aber leider ist nicht auf die Frage –

+1

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

16

Dies ist eine gcc-Erweiterung, bekannt als "Labels als Werte". Link to gcc documentation.

In dieser Erweiterung ist && ein unärer Operator, der auf ein Label angewendet werden kann. Das Ergebnis ist ein Wert vom Typ void *. Dieser Wert kann später in einer goto-Anweisung dereferenziert werden, damit die Ausführung zu diesem Label springt. Auch Zeigerarithmetik ist bei diesem Wert erlaubt.

Das Etikett muss in der gleichen Funktion sein; oder in einer einschließenden Funktion, falls der Code auch die gcc-Erweiterung von "verschachtelten Funktionen" verwendet.

Hier ist ein Beispielprogramm, wo die Funktion eine Zustandsmaschine verwendet wird, zu implementieren:

#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; 
    } 
} 

Kompilieren und Ausführung:

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

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 "int" überschreitet. Vielleicht ist das aufgrund der Anforderung, dass beide in der gleichen Funktion sind, nicht möglich, aber "ptrdiff_t" wäre vielleicht besser. –

+0

Der richtige Typ ist tatsächlich 'ptrdiff_t'; Das ist nur ein Fehler im Handbuch (wahrscheinlich stammt es von den Tagen, an denen 'ptrdiff_t' und' int' fast immer vom selben Typ waren). Sie können dies selbst sehen, indem Sie 'void test (void) {a :; b :; __typeof (&& a - &&b) d; ptrdiff_t * pd = & d; int * qd = & d;} 'auf einer ABI, wo sie nicht vom selben Typ sind. – zwol