What does a && operator do when there is no left side in C?
Asked Answered
P

3

52

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.

Pot answered 6/9, 2016 at 21:9 Comment(10)
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??Suspiration
This looks kind of like GCC's computed gotos. Are varOne, varTwo, and varThree labels?Leonleona
@user2357112: Good point. Once more a minimal reproducible example would have helped.Suspiration
Hehe.. everyday there is something new..Melanson
@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.Rexford
@hugomg: Good point. Anyway, that is no standard C and very likely a compiler extension.Suspiration
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?)Eunuchoidism
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.Corbin
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".Cochrane
@Corbin 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.Marcimarcia
H
64

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 &&.

Hathorn answered 7/9, 2016 at 0:3 Comment(3)
so the next question is why its *arr[1][8432]Effeminacy
@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 ...Hathorn
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.Tonisha
E
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
Enchain answered 7/9, 2016 at 0:3 Comment(2)
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.Enchain
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.Tonisha
O
-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.

Openhanded answered 6/9, 2016 at 21:50 Comment(7)
It's a gcc extension. See the other answers.Hathorn
This is correct information, but unfortunately, is not related to the questionEnchain
Ouch, meta effect gone bad. This answer is (or better was) good before it got closed and reopened again, changing in the mean time.Eunuchoidism
@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 :)Openhanded
@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.Maddis
@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 – though I think the latter would actually be a loss of value for SO… Maybe move it to documentation?Maddis
@DidierL Thanks for the comment and the nice words! :) I'll go with adding this to the documentation and will keep the answer hereOpenhanded

© 2022 - 2024 — McMap. All rights reserved.