【问题标题】:Can (a==1 && a==2 && a==3) ever evaluate to true in C or C++?(a==1 && a==2 && a==3) 在 C 或 C++ 中可以评估为真吗?
【发布时间】:2019-05-31 04:34:32
【问题描述】:

我们知道它可以在JavaJavaScript 中使用。

但问题是,在 C 或 C++ 中,永远下面的条件能否评估为真

if(a==1 && a==2 && a==3) 
    printf("SUCCESS");

编辑

如果a 是一个整数

【问题讨论】:

标签: c++ c


【解决方案1】:

如果a 是原始类型(即所有==&& 运算符都是内置的)并且您处于已定义的行为中,并且其他线程无法在中间修改a执行(这在技术上是未定义行为的情况 - 请参阅 cmets - 但我还是把它留在这里,因为它是 Java 问题中给出的示例),并且没有涉及预处理器魔法(请参阅选择的答案),那么我不相信有任何方法可以评估为真。但是,正如您从该条件列表中看到的那样,根据使用的类型和代码的上下文,在许多情况下,该表达式可以计算为真。

【讨论】:

  • 如果线程可以在执行过程中修改值并且a 是原始类型,那么您将有未定义的行为,因此您之前处于已定义行为的条件已经涵盖了该条件。跨度>
  • @FrançoisAndrieux 我假设你就在这里,我对 UB 了解得不够多,无法争辩。
  • @FrançoisAndrieux 如果 a 是 volatile int(并由某些外部硬件写入),这是否也适用?
  • @MaxLanghof 不,但在这种情况下,它不是另一个线程在编写,并且无论如何都不会被冗余条件覆盖。编辑:如果使用volatile 尝试消除与另一个线程而不是映射的硬件设备的竞争条件,请参阅this answer,了解为什么volatile 不能防止竞争条件。
  • 执行过程中修改a不一定是UB。请参阅我对 volatile 案例的回答。
【解决方案2】:

在 C 中,是的,它可以。如果a 未初始化(即使没有UB,正如here 所讨论的那样),它的值是不确定的,读取它会得到不确定的结果,因此将它与其他数字进行比较也会得到不确定的结果。

作为直接结果,a 可以在某一时刻将true1 进行比较,然后在下一刻将true2 进行比较。它不能同时保持这两个值,但没关系,因为它的值是不确定的。

不过,在实践中,我会惊讶地看到您描述的行为,因为在两次比较之间的内存中实际存储没有真正的原因发生变化。


在 C++ 中,有点。上面的情况仍然是正确的,但是读取一个不确定的值在 C++ 中总是一个未定义的操作,所以真的所有的赌注都没有了。

允许优化极大地破坏你的代码,当你做未定义的事情时,这很容易导致各种混乱。

所以在 C++ 中看到这个结果比在 C 中看到这个结果我不会那么惊讶,但是,如果我这样做了,那将是一个没有目的或意义的观察,因为无论如何都应该完全忽略具有未定义行为的程序。


当然,在这两种语言中,您都可以使用“技巧”,例如 #define a (x++),尽管这些似乎不符合您的问题的精神。

【讨论】:

  • 这就是我想要表达的观点。我知道在 C 中,该值固定为某个不确定的初始值,并且与 C++ 不同,它必须是自洽的。
  • @FrançoisAndrieux 在我看来,不确定和自洽的概念完全不同,除非你能找到相反的措辞。不定就是不定!要么有确定性的价值,要么没有(即使我们是否被允许观察它!)
  • @qrdl 不,它有一个不确定的值。这是另一种说没有确定价值的英语方式。 Indeterminate 并不意味着“任意”或“随机”或“剩余”,它的意思是 indeterminate
  • @LightnessRacesinOrbit 好的,我错了,我在附件 J.2 中找到了 C11 所说的内容:The behavior is undefined in the following circumstances: [...] The value of an object with automatic storage duration is used while it is indeterminate。所以访问不确定值确实是UB。
  • 似乎有一个defect report 关于这个。我不清楚 C 开发人员是否在抱怨不确定值的性质或要求标准来澄清它,但这似乎暗示 C 中的不确定值就像 C++ 中的不确定值并且不需要是 self -持续的。编辑:委员会的答复包括:“问题 1 的答案是“是”,在所描述的条件下未初始化的值可能会改变其值。” 我觉得这很权威。
【解决方案3】:

取决于您对“a 是整数”的定义:

int a__(){ static int r; return ++r; }
#define a a__()  //a is now an expression of type `int`
int main()
{
    return a==1 && a==2 && a==3; //returns 1
}

当然:

int f(int b) { return b==1&&b==2&&b==3; }

将始终return 0; 并且优化器通常会将检查替换为该检查。

【讨论】:

    【解决方案4】:

    如果我们把宏观魔术放在一边,我可以看到一种可以肯定回答这个问题的方法。但它需要的不仅仅是标准 C。假设我们有一个扩展允许使用 __attribute__((at(ADDRESS))); 属性,该属性将变量放置在某个特定的内存位置(例如,在某些 ARM 编译器中可用,如 ARM GCC) .假设我们在地址ADDRESS 处有一个硬件计数器寄存器,每次读取都会递增。然后我们可以这样做:

    volatile int a __attribute__((at(ADDRESS)));
    

    volatile 强制编译器在每次执行比较时生成寄存器读取,因此计数器将递增 3 次。如果计数器的初始值为1,则语句将返回true。

    附:如果您不喜欢at 属性,可以使用链接器脚本将a 放入特定的内存部分来实现相同的效果。

    【讨论】:

    • 这很聪明。
    【解决方案5】:

    以下程序随机打印seen: yesseen: no,这取决于在主线程执行中的某个时刻(a == 0 && a == 1 && a == 2) 是否被评估为true

    #include <pthread.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    _Atomic int a = 0;
    _Atomic int relse = 0;
    
    void *writer(void *arg)
    {
      ++relse;
      while (relse != 2);
      for (int i = 100; i > 0; --i)
        {
          a = 0;
          a = 1;
          a = 2;
        }
      return NULL;
    }
    
    int main(void)
    {
      int seen = 0;
      pthread_t pt;
      if (pthread_create(&pt, NULL, writer, NULL)) exit(EXIT_FAILURE);
      ++relse;
      while (relse != 2);
      for (int i = 100; i > 0; --i)
        seen |= (a == 0 && a == 1 && a == 2);
      printf("seen: %s\n", seen ? "yes":"no");
      pthread_join(pt, NULL);
      return 0;
    }
    

    据我所知,这在任何时候都不包含未定义的行为,并且a 是整数类型,正如问题所要求的那样。 显然这是一个竞争条件,因此是否打印seen: yesseen: no 取决于程序运行的平台。在 Linux 上,x86_64、gcc 8.2.1 两个答案都会定期出现。如果不起作用,请尝试增加循环计数器。

    【讨论】:

    • 不知道应该如何对依赖调度器的行为进行分类..
    • @EugeneSh。 实现定义我假设。另外,我还没有在单处理器机器上测试过这个,所以我希望它是并行运行而不是同时运行。
    • 是的,但显然即使在相同的实现上它也是不一致的(嗯,看似不一致)。但它不是未定义的,我很确定
    • @EugeneSh。老实说,我从更高的循环计数器开始,对seen: yes 答案出现的一致性感到震惊。我有点惊讶于这场比赛是多么容易。
    猜你喜欢
    • 2023-04-04
    • 2018-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-26
    • 2017-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多