Java和C编译器的代码表现如何不同?
我有这段代码,我在Java和C上运行了这段代码,但它们给出了两个不同的结果。 是什么让他们跑得与众不同Java和C编译器的代码表现如何不同?,java,c,Java,C,我有这段代码,我在Java和C上运行了这段代码,但它们给出了两个不同的结果。 是什么让他们跑得与众不同 x=10;y=10;z=10; y-=x--; z-=--x; x-=--x-x--; 值X的Java输出为:8,而C的输出为6 这两个编译器对于增量选项的行为如何不同?在C++中,结果是不确定的,即没有指定或保证是一致的。编译器可以自由地根据.随时做最适合的事情。 我怀疑Java[和C#等]也是如此。我不确定,但我猜这是因为Java在计算-=运算符之前,在最后一个x上计算后减量,而C++在
x=10;y=10;z=10;
y-=x--;
z-=--x;
x-=--x-x--;
值X的Java输出为:8,而C的输出为6
这两个编译器对于增量选项的行为如何不同?
在C++中,结果是不确定的,即没有指定或保证是一致的。编译器可以自由地根据.随时做最适合的事情。我怀疑Java[和C#等]也是如此。我不确定,但我猜这是因为Java在计算-=运算符之前,在最后一个x上计算后减量,而C++在完成表达式的其余部分之后计算-=第一和后减量。当你说这个代码被认为是C程序时,你的错误是“代码> 6 。 作为一个C程序,这是。您的编译器刚好得到6,但您也可以得到24,分段错误或编译时错误 见第6.5.2节: 在上一个序列点和下一个序列点之间,对象应有其存储值 通过表达式的评估最多修改一次。此外,先验值 应为只读,以确定要存储的值。71)
--x-x--
被本段明确禁止
编辑:
Aaron Digulla在评论中写道:
它真的没有定义吗
您是否注意到我链接到C99标准,并指出了一段说这是未定义的
gcc-Wall(GCC4.1.2)并没有对此抱怨,我怀疑任何编译器都会拒绝这段代码
该标准将某些行为描述为“未定义”,这正是因为并非所有C程序无意义的方式都能在编译时可靠地检测到。如果您认为“无警告”应该意味着一切正常,那么您应该切换到C以外的另一种语言。许多现代语言都有更好的定义。当我有选择的时候我会使用,但是还有无数其他定义良好的语言
它返回6是有原因的,您应该能够解释它
我没有注意到你解释为什么这个表达式的值为6。我希望你不要花太多时间写它,因为对我来说它返回0
Macbook:~ pascalcuoq$ cat t.c
#include <stdio.h>
int main(int argc, char **argv)
{
int y;
printf("argc:%d\n", argc);
y = --argc - argc--;
printf("y:%d\n", y);
return 0;
}
Macbook:~ pascalcuoq$ gcc t.c
Macbook:~ pascalcuoq$ ./a.out 1 2 3 4 5 6 7 8 9
argc:10
y:0
亚伦还写道:
作为一名工程师,您应该仍然能够解释它返回一个结果或另一个结果的原因
没错!我给出了可能得到6的最简单解释:结果在C99中被明确指定为未定义的行为,在早期的标准中也是如此
以及:
最后,请展示一个编译器,它警告这个构造
据我所知,没有编译器警告*(&x-1)
其中x
由intx代码>。您是否声称这个构造是有效的C,并且一个好的工程师应该能够预测结果,因为没有编译器警告它?这个构造是未定义的,就像正在讨论的那个一样
最后,如果您绝对需要警告,认为存在问题,请考虑使用一个验证工具,如。它需要做出一些标准中没有的假设来捕获一些现有实践,但它正确地警告了
--x-x--
和大多数其他未定义的C行为。那么。。。你认为哪个是正确的,你的理由是什么
我相信x
对于前三个步骤是非常确定的
x = 10
x is decremented (its initial value is used first)
x is decremented again (its resulting value is used after)
现在x==8
。但是看看你在这里对它做了什么(请原谅插入了人性化的空格):
这可以被编译成(如果我必须在我的语言中包含++
和--
操作符,我会这么做-副作用首先被识别出来,然后作为一个整体从语句的前后移除):
给出x==8
的结果。或者它被编译为(语句首先被子表达式缩减):
或者子表达式可能会以另一种方式降落:
t1 = x-- // t1 = 8, x = 7
t2 = --x // t2 = 6, x = 6
t = t2 - t1 // t = 6 - 8 = -2
x -= t // x = 8
在这种情况下,如果没有对操作员行为的正式描述,谁能说哪一个是正确的呢?如何评估术语?右边的--x-x--
对Java和C的计算结果都是0,但它会改变x
。所以问题是:-=
是如何工作的?是否在计算右侧(RHS)之前读取x
,然后减去RHS,还是在计算RHS之后读取。那你有吗
tmp = x // copy the value of x
x = tmp - (--x - x--) // complicated way to say x = x
或
在Java中,规则如下:
形式为E1 op=E2的复合赋值表达式等价于E1=(T)((E1)op(E2)),其中T是E1的类型,但E1仅计算一次。(见附件)
这意味着将复制的值,因此前后递减无效。您的C编译器可能使用不同的规则
对于C,可能有帮助:
寓意在于,在任何语言中,编写依赖于求值顺序的代码都是一种糟糕的编程实践
[编辑]Pascal Cuoq(见下文)坚持认为标准中说结果未定义。这可能是正确的:我盯着他从标准中抄出来的部分看了几分钟,不明白那句话说了什么。我想我并不是这里唯一的人:)所以我去看看我为硕士论文开发的C解释器是如何工作的。它不符合标准,但我理解它的工作原理。我猜,我是海森堡类型的人:我可以有任何一种精度,但不能两者都有;)无论如何
解析此构造时,您会得到以下解析树:
+---- (-=) ----+
v -= v
x +--- (-) ----+
v v
PREDEC x POSTDEC x
该标准规定,修改x
三次(一次在左侧,两次在两个减量操作中),会导致x
未定义。可以但是编译器是一个确定性程序,所以当它接受一些输入时,它将
t1 = --x // t1 = 7, x = 7
t2 = x-- // t2 = 7, x = 6
t = t1 - t2 // t = 7 - 7 = 0
x -= t // x = 6
t1 = x-- // t1 = 8, x = 7
t2 = --x // t2 = 6, x = 6
t = t2 - t1 // t = 6 - 8 = -2
x -= t // x = 8
tmp = x // copy the value of x
x = tmp - (--x - x--) // complicated way to say x = x
tmp = (--x - x--) // first evaluate RHS, from left to right, which means x -= 2.
x = x - tmp // substract 0 from x
+---- (-=) ----+
v -= v
x +--- (-) ----+
v v
PREDEC x POSTDEC x
#include <stdio.h>
int main() {
int x = 8;
x -= --x - x--;
printf("x=%d\n", x);
}
.loc 1 4 0
movl $8, -4(%rbp) ; x = 8
.loc 1 5 0
subl $1, -4(%rbp) ; x--
movl $0, %eax ; tmp = 0
subl %eax, -4(%rbp) ; x -= tmp
subl $1, -4(%rbp) ; x--
.loc 1 6 0
movl -4(%rbp), %esi ; push `x` into the place where printf() expects it
x -= --x - x--;