采用以下C代码( K& R第77页):
push(pop() - pop()); /* WRONG */
本书说,由于-
和/
不是可交换运算符,因此需要评估2 pop
个函数的顺序(显然,要获得正确的结果)。 ..因此你必须先将第一个函数的结果放在一个变量中然后继续运算,如下所示:
op2 = pop();
push(op2 - pop());
显然这是因为编译器无法保证评估函数的顺序(...为什么?)
我的问题是,C#做同样的事吗?在使用C#时,我是否需要担心这种事情?就此而言,是否还有其他更高级别的语言?
答案 0 :(得分:11)
在C#中,它从左到右:http://blogs.msdn.com/oldnewthing/archive/2007/08/14/4374222.aspx
Re:C ++订单
显然这是因为编译器无法保证评估函数的顺序(...为什么?)
任何特定的编译器都可以保证订单。问题是语言规范没有指定顺序,所以每个编译器都可以做任何想做的事情。这意味着如果要保证排序,则必须在两个方法调用之间添加一个序列点。
答案 1 :(得分:5)
回答你关于为什么C没有定义操作顺序的问题,这只是因为C的发明者认为给编译器实现者提供优化表达式评估的机会是有价值的。他们还认为,这比给予程序员表达评估的确定性更有价值。
请记住,当C最初开发时,机器的能力远远低于今天,并且更有兴趣为编译器提供优化余地。今天,通常会给予更安全,更可预测的代码更多的权重。
答案 2 :(得分:2)
来自Fabulous Adventures In Coding: Precedence vs Associativity vs Order:
另一种看待它的方法是C#中的规则不是“先括号”,而是括号括起来然后递归地应用规则“评估左侧,然后评估右侧,然后执行操作” ”
答案 3 :(得分:2)
不,C#没有做同样的事情。它不像C那样使用评估边界,其中边界之间的执行顺序是未定义的。表达式从左到右进行评估,就像您期望的那样。
如果您不确定执行顺序,或者您想使代码更清晰,则应使用局部变量作为中间结果。局部变量分配非常便宜,因为它们在堆栈上分配,在某些情况下,编译器甚至能够将变量放入寄存器中,以便根本不分配它。此外,根据表达式的外观,编译器可能需要使用局部变量来存储intermediade结果。
答案 4 :(得分:1)
在所有情况下,评估顺序在C#中都是明确定义的,并且从左到右。来自C#语言规范(§7.3):
表达式中运算符的求值顺序由运算符的优先级和关联性决定(第7.2.1节)。 表达式中的操作数从左到右进行计算。例如,在F(i)+ G(i ++)* H(i)中,使用旧值i调用方法F,然后使用旧值i调用方法G,最后调用方法H与i的新价值。这与运算符优先级
分开并且无关
对于C ++,并不是订单无法定义;允许编译器未定义允许编译器更好地优化代码。
答案 5 :(得分:1)
这本书说,因为 - 和/不是可交换的运算符,所以评估2个pop函数的顺序是必要的(显然,为了得到正确的结果)......因此你必须把结果放在首先在变量中起作用,然后继续算术。
这不太正确。 K& R允许重新排列交换运算符(done away with in ANSI C)。因为suibtraction 不可交换,所以它不能重新安排......至少在那个规则下。
(联合国)幸运的是,C 也没有define the order of evaluation(在a fairly small scope之外) - 这意味着编译器可以调用这些函数以任何顺序(只要在调用pop() - pop()
之前完全评估push()
的结果)
其中 - 在这种情况下,会导致同样的问题 - 但原因不同。
答案 6 :(得分:0)
我相信在C#中,参数列表按从左到右的顺序进行评估。
答案 7 :(得分:0)
让我感到惊讶,但显然C#做了“正确”的事情,并从左到右进行评估:
void Main()
{
Console.WriteLine(Pop() - Pop()); // Prints -1
}
// Define other methods and classes here
bool switchVar = true;
int Pop()
{
int ret;
if (switchVar)
ret = 1;
else
ret = 2;
switchVar = !switchVar;
return ret;
}