【问题标题】:What does a && operator do when there is no left side in C?当 C 中没有左侧时,&& 运算符会做什么?
【发布时间】:2016-09-06 21:09:42
【问题描述】:

我看到一个 C 语言程序,其代码如下:

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

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

我对@9​​87654324@ 的作用感到困惑,因为它的左侧没有任何内容。默认情况下它是否评估为空?还是这是特例?

编辑: 添加了更多信息,以使问题/代码对我的问题更加清晰。 谢谢大家的帮助。这是 gcc 特定扩展的一个案例。

【问题讨论】:

  • 这不是一个&&,而是两个&。并且在 C 中无效,因为 & 的结果是另一个 & 的无效表达式。确定那不是 C++ 代码??
  • 这看起来有点像 GCC 的 computed gotos。是varOnevarTwovarThree 标签吗?
  • @user2357112:好点。再一次,minimal reproducible example 会有所帮助。
  • @Olaf:不,因为&& 将始终被词法分析器视为一个单元。这与 return0 之类的东西将被视为单个标识符而不是 return 0 的原因相同。
  • 我投票决定重新打开,因为在这里关闭为“离题”对我来说似乎是错误的:这肯定是关于 GCC 的计算 goto,从快速搜索来看,我们似乎没有问题尤其是在处理这个问题。有这样一个问题会很好,可以节省未来的“这是一个计算的 goto”..“什么是计算的 goto”讨论。虽然它应该被适当地标记(gcc 和语言扩展?)

标签: c gcc goto language-extension


【解决方案1】:

这是一个 gcc 特定的扩展,一个一元 && 运算符,可应用于标签名称,将其地址作为 void* 值生成。

作为扩展的一部分,goto *ptr; 是允许的,其中ptrvoid* 类型的表达式。

在 gcc 手册中记录了here

您可以获取当前函数中定义的标签的地址(或 一个包含函数)与一元运算符&&。值有 输入void *。这个值是一个常数,可以在任何地方使用 该类型的常量是有效的。例如:

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

要使用这些值,您需要能够跳转到一个。这个完成了 使用计算出的 goto 语句,goto *exp;。例如,

goto *ptr;

任何void * 类型的表达式都是允许的。

正如 zwol 在评论中指出的那样,gcc 使用 && 而不是更明显的 &,因为标签和具有相同名称的对象可以同时可见,如果 & 意味着,则使 &foo 可能不明确“标签地址”。标签名称占据它们自己的命名空间(不是 C++ 意义上的),并且只能出现在特定的上下文中:由 labeled-statement 定义,作为 goto 语句的目标,或者,对于 gcc , 作为一元 && 的操作数。

【讨论】:

  • 所以下一个问题是为什么它是*arr[1][8432]
  • @pm100:不看上下文就不知道了。它是 void* 指针的数组 8432 的数组 1。前3个被初始化;大概其余的都有稍后分配给它们的值。我不知道为什么它是二维数组而不是一维数组(可能是一些伪引用传递黑客)。但是 OP 没有问这个,所以 ...
  • 如果你想知道,扩展使用&&label而不是更自然的&label,因为你可以在同一个函数中同时拥有同名的标签和变量。跨度>
【解决方案2】:

这是一个 gcc 扩展,称为“标签即值”。 Link to gcc documentation.

在这个扩展中,&& 是一个可以应用于标签的一元运算符。结果是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

【讨论】:

  • gcc 文档中的示例使用int 作为减法结果的类型 - 不确定如果差异超出int 的范围会发生什么。也许这实际上是不可能的,因为两者都需要在同一个函数中,但也许ptrdiff_t 会更好。
  • 正确的类型其实是ptrdiff_t;这只是手册中的一个错误(可能可以追溯到ptrdiff_tint 几乎总是相同类型的日子)。您可以通过在 ABI 上编译 void test(void) { a:; b:; __typeof (&amp;&amp;a - &amp;&amp;b) d; ptrdiff_t *pd = &amp;d; int *qd = &amp;d; } 来亲自查看它们的类型不同。
【解决方案3】:

我不知道任何在 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 } };

逻辑与

逻辑与运算符更简单,与上面的运算符不同,它是一个二元运算符,这意味着它需要一个左右操作数。它的工作方式是评估左右操作数并返回 true,如果两者都是 true,或者如果它们不是 bool,则大于 0。

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

按位与

在 C 语言中 & 符号的另一种用法是执行按位与。它与逻辑 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)当两个 & 符号放在后面时,r 值引用一种。 2) 在模板类型或自动扣除类型之后放置两个 & 符号时的通用引用。

我认为由于某种扩展,您的代码可能只能在您的编译器中编译。我在想这个https://en.wikipedia.org/wiki/Digraphs_and_trigraphs#C,但我怀疑是这样。

【讨论】:

  • 这是一个 gcc 扩展。查看其他答案。
  • 这是正确的信息,但不幸的是,与问题无关
  • 哎呀,元效果坏了。在关闭并再次重新打开之前,这个答案(或更好的是)很好,同时发生了变化。
  • @KeithThompson 我并不特别了解扩展程序,但我确实提到它可能是一个扩展程序,无论如何感谢您提供的信息:)
  • @Daniel 这个问题在 meta 上讨论过吗?我找不到它。有点不幸的是,问题被修改为使这个答案无效。
猜你喜欢
  • 1970-01-01
  • 2018-01-12
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2019-05-19
  • 2011-09-14
相关资源
最近更新 更多