50

次のようなコードを持つ C のプログラムを見ました。

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

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

&&左側には何もないので、 が何をするのか混乱しています。デフォルトで null として評価されますか? それともこれは特殊なケースですか?

編集:私の質問の質問/コードをより明確にするために、さらに情報を追加しました。助けてくれてありがとう。これは、gcc 固有の拡張の場合です。

4

3 に答える 3

62

これは gcc 固有の拡張であり&&、ラベル名に適用できる単項演算子であり、そのアドレスをvoid*値として生成します。

拡張機能の一部として、は type の式としてgoto *ptr;許可されます。ptrvoid*

gccマニュアルのこちらに記載されています。

単項演算子を使用して、現在の関数 (またはそれを含む関数) で定義されているラベルのアドレスを取得できます&&。値の型はvoid *です。この値は定数であり、その型の定数が有効な場合はどこでも使用できます。例えば:

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

これらの値を使用するには、その値にジャンプできる必要があります。これは、計算された goto ステートメントで行われgoto *exp;ます。例えば、

goto *ptr;

タイプの任意の式void *が許可されます。

zwol がコメントで指摘しているように、ラベルと同じ名前のオブジェクトが同時に表示される可能性があるため、gcc は&&より明白なものではなく使用し、 「ラベルのアドレス」を意味する場合があいまいになる可能性があります。ラベル名は (C++ の意味ではなく) 独自の名前空間を占有し、特定のコンテキストでのみ表示できます:ステートメントのターゲットとして、または gcc の場合は unary のオペランドとして、labeled-statementによって定義されます。&&foo&goto&&

于 2016-09-07T00:03:06.793 に答える
18

これは、「値としてのラベル」として知られる gcc 拡張機能です。gcc ドキュメントへのリンク

この拡張では、 はlabel&&に適用できる単項演算子です。結果は type の値です。この値は後でステートメントで逆参照され、実行がそのラベルにジャンプする原因になります。また、この値に対してポインター演算が許可されます。void *goto

ラベルは同じ関数内にある必要があります。または、コードが「ネストされた関数」の gcc 拡張も使用している場合は、囲んでいる関数で。

以下は、この機能を使用してステート マシンを実装するサンプル プログラムです。

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

コンパイルと実行:

$ gcc -o x x.c && ./x
Foo
Qux
Foo
Qux
Bar
Foo
Qux
Qux
Bar
Foo
于 2016-09-07T00:03:45.320 に答える
-7

C でこのように機能する演算子を私は知りません。コンテキストに応じて、C のアンパサンドはさまざまな意味を持ちます。

Address-Of 演算子

左辺値の直前、例えば

int j;
int* ptr = &j;

上記のコードでは、ptrは j のアドレスを格納します。このコンテキストでの & は、任意の左辺値のアドレスを取得しています。以下のコードは、そのように書かれていれば、私にとってより意味のあるものだったでしょう。

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

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

論理積

論理 AND 演算子は、上記の演算子とは異なり、より単純です。二項演算子であり、左右のオペランドが必要です。これが機能する方法は、左右のオペランドを評価し、両方が true の場合は true を返し、bool でない場合は 0 より大きいことです。

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

ビット演算 AND

C でアンパサンドを使用するもう 1 つの方法は、ビットごとの AND を実行することです。アンパサンドを 1 つだけ使用し、ビット レベルで AND 演算を実行する点を除いて、論理 AND 演算子と似ています。

数値があり、それが以下に示すバイナリ表現にマップされていると仮定しましょう。AND 演算は次のように機能します。

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

C++ の世界では、事態はさらに複雑になります。アンパサンドは、参照型を示すために型の後に配置できます (あまり強力ではありませんが安全な種類のポインターと考えることができます)。その後、1) 2 つのアンパサンドが後に配置されると、r-value 参照でさらに複雑になります。タイプ。2) テンプレート タイプまたは自動推定タイプの後に 2 つのアンパサンドが配置されている場合のユニバーサル参照。

あなたのコードは、何らかの拡張機能により、おそらくコンパイラでのみコンパイルされると思います。私はこれを考えていましたhttps://en.wikipedia.org/wiki/Digraphs_and_trigraphs#Cしかし、そうではないかと思います。

于 2016-09-06T21:50:46.590 に答える