【发布时间】:2019-05-31 04:34:32
【问题描述】:
我们知道它可以在Java 和JavaScript 中使用。
但问题是,在 C 或 C++ 中,永远下面的条件能否评估为真?
if(a==1 && a==2 && a==3)
printf("SUCCESS");
编辑
如果a 是一个整数。
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
我们知道它可以在Java 和JavaScript 中使用。
但问题是,在 C 或 C++ 中,永远下面的条件能否评估为真?
if(a==1 && a==2 && a==3)
printf("SUCCESS");
编辑
如果a 是一个整数。
【问题讨论】:
如果a 是原始类型(即所有== 和&& 运算符都是内置的)并且您处于已定义的行为中,并且其他线程无法在中间修改a执行(这在技术上是未定义行为的情况 - 请参阅 cmets - 但我还是把它留在这里,因为它是 Java 问题中给出的示例),并且没有涉及预处理器魔法(请参阅选择的答案),那么我不相信有任何方法可以评估为真。但是,正如您从该条件列表中看到的那样,根据使用的类型和代码的上下文,在许多情况下,该表达式可以计算为真。
【讨论】:
a 是原始类型,那么您将有未定义的行为,因此您之前处于已定义行为的条件已经涵盖了该条件。跨度>
volatile int(并由某些外部硬件写入),这是否也适用?
volatile 尝试消除与另一个线程而不是映射的硬件设备的竞争条件,请参阅this answer,了解为什么volatile 不能防止竞争条件。
a不一定是UB。请参阅我对 volatile 案例的回答。
在 C 中,是的,它可以。如果a 未初始化(即使没有UB,正如here 所讨论的那样),它的值是不确定的,读取它会得到不确定的结果,因此将它与其他数字进行比较也会得到不确定的结果。
作为直接结果,a 可以在某一时刻将true 与1 进行比较,然后在下一刻将true 与2 进行比较。它不能同时保持这两个值,但没关系,因为它的值是不确定的。
不过,在实践中,我会惊讶地看到您描述的行为,因为在两次比较之间的内存中实际存储没有真正的原因发生变化。
在 C++ 中,有点。上面的情况仍然是正确的,但是读取一个不确定的值在 C++ 中总是一个未定义的操作,所以真的所有的赌注都没有了。
允许优化极大地破坏你的代码,当你做未定义的事情时,这很容易导致各种混乱。
所以在 C++ 中看到这个结果比在 C 中看到这个结果我不会那么惊讶,但是,如果我这样做了,那将是一个没有目的或意义的观察,因为无论如何都应该完全忽略具有未定义行为的程序。
当然,在这两种语言中,您都可以使用“技巧”,例如 #define a (x++),尽管这些似乎不符合您的问题的精神。
【讨论】:
The behavior is undefined in the following circumstances: [...] The value of an object with automatic storage duration is used while it is indeterminate。所以访问不确定值确实是UB。
取决于您对“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; 并且优化器通常会将检查替换为该检查。
【讨论】:
如果我们把宏观魔术放在一边,我可以看到一种可以肯定回答这个问题的方法。但它需要的不仅仅是标准 C。假设我们有一个扩展允许使用 __attribute__((at(ADDRESS))); 属性,该属性将变量放置在某个特定的内存位置(例如,在某些 ARM 编译器中可用,如 ARM GCC) .假设我们在地址ADDRESS 处有一个硬件计数器寄存器,每次读取都会递增。然后我们可以这样做:
volatile int a __attribute__((at(ADDRESS)));
volatile 强制编译器在每次执行比较时生成寄存器读取,因此计数器将递增 3 次。如果计数器的初始值为1,则语句将返回true。
附:如果您不喜欢at 属性,可以使用链接器脚本将a 放入特定的内存部分来实现相同的效果。
【讨论】:
以下程序随机打印seen: yes 或seen: 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: yes 或seen: no 取决于程序运行的平台。在 Linux 上,x86_64、gcc 8.2.1 两个答案都会定期出现。如果不起作用,请尝试增加循环计数器。
【讨论】:
seen: yes 答案出现的一致性感到震惊。我有点惊讶于这场比赛是多么容易。