50

I saw a program in C that had code like the following:

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

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

I am confused about what the && does because there is nothing to the left of it. Does it evaluate as null by default? Or is this a special case?

Edit: Added some more information to make the question/code more clear for my question. Thank you all for the help. This was a case of the gcc specific extension.

Joshua
  • 547
  • 4
  • 6
  • 11
    That's not one `&&` but two `&`. And invalid in C, as the result of `&` is no valid expression for another `&`. Sure that is not C++ code?? – too honest for this site Sep 06 '16 at 21:12
  • 37
    This looks kind of like GCC's [computed gotos](https://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Labels-as-Values.html). Are `varOne`, `varTwo`, and `varThree` labels? – user2357112 Sep 06 '16 at 21:12
  • 13
    @user2357112: Good point. Once more a [mcve] would have helped. – too honest for this site Sep 06 '16 at 21:14
  • Hehe.. everyday there is something new.. – Eugene Sh. Sep 06 '16 at 21:17
  • 13
    @Olaf: No, because `&&` will always be treated as a single unit by the lexer. Its the same reason why something like `return0` will be treated as a single identifier instead of as `return 0`. – hugomg Sep 06 '16 at 21:23
  • 1
    @hugomg: Good point. Anyway, that is no standard C and very likely a compiler extension. – too honest for this site Sep 06 '16 at 21:30
  • 12
    I voted to reopen, because closing as "off-topic" seems wrong to me here: This is very certainly about GCC's computed gotos, and from a quick search it seems we don't have a question that's dealing with that in particular. Having such a question would be nice, saving potentially future "that's a computed goto" .. "what's a computed goto" discussions. Though it should be tagged appropriately (gcc and language-extension?) – Daniel Jour Sep 06 '16 at 22:10
  • 1
    Note that these are only useful for computed gotos - not for comparing the sizes of different blocks of code. gcc has a tendency to move operations freely across label boundaries, so you cannot for instance label multiple implementations of the same algorithm and only use the smallest computed one (eliminating the others via dead code elimination) to optimize for size on different platforms where one algorithm may be smaller while a different algorithm is smaller on another platform. – technosaurus Sep 07 '16 at 03:46
  • This is a pointer to a temporary pointer. Temporary pointer is an rvalue. You can take a pointer of lvalue only. So the correct answer to the question should be "this leads to compile-time error unless unary operator& is overloaded". – polkovnikov.ph Sep 07 '16 at 09:50
  • 1
    @technosaurus Not only that, but computed gotos are *not* guaranteed to be implemented as actual instruction pointers at all. One of the reasons GCC tells you not to share them between scopes is because it will often implement them as the equivalent of a `switch` block, so the "pointer" is actually just a `case` index and doesn't actually point to anything. – Alex Celeste Sep 07 '16 at 14:54

3 Answers3

62

It's a gcc-specific extension, a unary && operator that can be applied to a label name, yielding its address as a void* value.

As part of the extension, goto *ptr; is allowed where ptr is an expression of type void*.

It's documented here in the gcc manual.

You can get the address of a label defined in the current function (or a containing function) with the unary operator &&. The value has type void *. This value is a constant and can be used wherever a constant of that type is valid. For example:

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

To use these values, you need to be able to jump to one. This is done with the computed goto statement, goto *exp;. For example,

goto *ptr;

Any expression of type void * is allowed.

As zwol points out in a comment, gcc uses && rather than the more obvious & because a label and an object with the same name can be visible simultaneously, making &foo potentially ambiguous if & means "address of label". Label names occupy their own namespace (not in the C++ sense), and can appear only in specific contexts: defined by a labeled-statement, as the target of a goto statement, or, for gcc, as the operand of unary &&.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • so the next question is why its `*arr[1][8432]` – pm100 Sep 07 '16 at 00:12
  • @pm100: No idea without seeing some context. It's an array 1 of array 8432 of `void*` pointers. The first 3 are initialized; presumably the rest have values assigned to them later. I don't know why it's a 2-d array rather than a 1-d array (maybe some pseudo-pass-by-reference hackery). But the OP didn't ask about it, so ... – Keith Thompson Sep 07 '16 at 00:15
  • 4
    In case you're wondering, the extension uses `&&label` instead of the more natural `&label` because you can have both a label and a variable with the same name in the same function. – zwol Sep 07 '16 at 13:02
18

This is a gcc extension, known as "Labels as Values". Link to gcc documentation.

In this extension, && is a unary operator that can be applied to a label. The result is a value of type void *. This value may later be dereferenced in a goto statement to cause execution to jump to that label. Also, pointer arithmetic is permitted on this value.

The label must be in the same function; or in an enclosing function in case the code is also using the gcc extension of "nested functions".

Here is a sample program where the feature is used to implement a state machine:

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

Compiling and execution:

$ gcc -o x x.c && ./x
Foo
Qux
Foo
Qux
Bar
Foo
Qux
Qux
Bar
Foo
M.M
  • 138,810
  • 21
  • 208
  • 365
  • The example in the gcc docs uses `int` as the type for the result of subtraction - not sure what happens if the difference exceeds the range of `int`. Perhaps that is actually not possible due to the requirement for both to be in the same function, but maybe `ptrdiff_t` would be better. – M.M Sep 07 '16 at 00:11
  • The proper type is in fact `ptrdiff_t`; this is just an error in the manual (probably dates to the days when `ptrdiff_t` and `int` were almost always the same type). You can see this for yourself by compiling `void test(void) { a:; b:; __typeof (&&a - &&b) d; ptrdiff_t *pd = &d; int *qd = &d; }` on an ABI where they are not the same type. – zwol Sep 07 '16 at 13:19
-7

I'm not aware of any operator that works this way in C. Depending on the context, the ampersand in C can mean many different things.

Address-Of operator

Right before an lvalue, e.g.

int j;
int* ptr = &j;

In the code above, ptr stores the address of j, & in this context is taking the address of any lvalue. The code below, would have made more sense to me if it was written that way.

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

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

Logical AND

The logical AND operator is more simple, unlike the operator above, it's a binary operator, meaning it requires a left and right operand. The way it works is by evaluating the left and right operand and returning true, iff both are true, or greater than 0 if they are not bool.

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

Bitwise AND

Another use of the ampersand in C, is performing a bitwise AND. It's similar as the logical AND operator, except it uses only one ampersand, and performs an AND operation at the bit level.

Let's assume we have a number and that it maps to the binary representation shown below, the AND operation works like so:

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, things get more complicated. The ampersand can be placed after a type as to denote a reference type (you can think of it as a less powerful but safe kind of pointer), then things get even more complicated with 1) r-value reference when two ampersands are placed after a type. 2) Universal references when two ampersands are placed after a template type or auto deducted type.

I think your code probably compiles only in your compiler due to an extension of some sorts. I was thinking of this https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C but I doubt that's the case.

Fred Garnier
  • 7
  • 1
  • 2
  • It's a gcc extension. See the other answers. – Keith Thompson Sep 07 '16 at 00:08
  • 12
    This is correct information, but unfortunately, is not related to the question – M.M Sep 07 '16 at 00:09
  • 1
    Ouch, meta effect gone bad. This answer is (or better was) good before it got closed and reopened again, changing in the mean time. – Daniel Jour Sep 07 '16 at 15:20
  • 1
    @KeithThompson I wasn't aware of the extension in particular, but I did mention that it probably is an extension, in any case thanks for the info :) – Fred Garnier Sep 07 '16 at 17:56
  • @Daniel Was this question discussed on meta? I couldn't find it. It is a bit unfortunate that the question got modified in a way that invalidates this answer. – Didier L Sep 13 '16 at 22:24
  • @Fred, Don't get downhearted by the downvotes. If you want to do something about it, I think you have 2 possibilities: edit the answer to put a notice at the beginning that it is a gcc extension and refer to the other answers, or just delete it and get the [peer pressure badge](http://stackoverflow.com/help/badges/38/peer-pressure) – though I think the latter would actually be a loss of value for SO… Maybe move it to documentation? – Didier L Sep 13 '16 at 22:25
  • @DidierL Thanks for the comment and the nice words! :) I'll go with adding this to the documentation and will keep the answer here – Fred Garnier Sep 13 '16 at 23:08