我正在阅读《 K&R》第二章的结尾,我在理解两个特定的不相关示例代码行(及其后)以及本书的注释时遇到一些困难:
x = f() + g();
a[i] = i++;
Run Code Online (Sandbox Code Playgroud)
第一线 -我很容易理解该标准未指定+运营商的评估顺序,因此未指定是先评估f()还是g()先评估(这就是为什么我认为这个问题不是重复的)。我的困惑源于以下事实:如果我们查找C运算符优先级图表,就会引用具有从左到右的关联性的最高优先级的函数调用。现在,这f() 是否意味着必须先调用/评估g()?显然不是,但是我不知道自己缺少什么。
第二行 -关于数组是索引到的初始值i还是递增的值,同样存在类似的难题。但是,运算符优先级图表再次引用了具有左至右关联性的最高优先级的数组下标。因此,数组下标不是导致将数组下标到的初始值i并消除任何歧义的第一要评估的东西吗?显然不是,我缺少了一些东西。
我确实知道编译器可以自由决定表达式中何时发生副作用(当然是在序列点之间),并且如果在同一表达式中再次使用有问题的变量,可能会导致不确定的行为,但是在上面的示例中似乎任何歧义都可以通过函数调用和具有最高优先级并定义了从左到右的关联性的数组下标来清除,因此我看不到歧义。
我感觉我对关联性,运算符优先级和评估顺序有一些基本的误解,但我无法指出它是什么,与此主题相关的类似问题/答案不在我的支持范围之内。在这一点上彻底了解。
Rei*_*ica 21
第一行
从左到右的关联性表示诸如的表达式f()()()被评估为((f())())()。函数调用运算符的关联性()并未说明其与其他运算符(例如)的关系+。
(请注意,关联性仅对可嵌套的infix运算符(例如binary +,%或)有意义,。对于诸如函数调用或一元运算符之类的运算符,关联性通常毫无意义。)
第二行
运算符的优先级会影响解析,而不影响评估顺序。[]优先级高于的事实=意味着该表达式将解析为(a[i]) = (i++)。它几乎没有评价顺序。a[i]并且i++必须在分配前对两者进行评估,但是它们之间的相对顺序并没有说明。
希望消除混乱:
关联性控制解析,并告诉您a + b + c是解析为(a + b) + c(从左到右)还是解析为a + (b + c)(从右到左)。
优先级还控制解析,告诉你是否a + b * c被解析为(a + b) * c(+具有优先级高于*)或a + (b * c)(*优先级高于+)。
评估顺序控制着需要按哪个顺序评估哪些值。它的某些部分可能来自于关联性或优先级(必须在使用操作数之前对其进行评估),但很少有它们完全定义。
+运算符两侧的两个独立函数调用,它什么也没说。a[i] = i++。根本没有规则说出在表达式中何时i++将新结果准确地存储回i,这意味着没有规则告诉我们a[i]零件使用旧值还是新值。这就是为什么此表达式未定义的原因。优先级告诉您当您有两个可能适用的不同运算符时会发生什么。在中a + b * c,+还是*先申请?在中*p++,*还是++先申请?优先回答这些问题。
关联性告诉您,当您拥有两个可能同时应用的相同运算符(通常是一行相同的运算符)时会发生什么。在中a + b + c,哪个+先申请?这就是关联性的答案。
但是,这些问题的答案(即优先级和关联性规则提供的答案)适用范围很窄。他们告诉您您想知道的两个运算符中的哪一个首先要应用,但是他们并没有告诉您有关较大表达式或您想知道的运算符“下方”的较小子表达式的任何信息。(例如,如果我写了(a - b) + (c - d) * (e - f),没有规则说哪个减法首先发生。)
最重要的是,优先级和关联性不能完全确定评估顺序。再次以稍微不同的方式说:优先级和关联性部分确定某些表达式中的求值顺序,但它们不能完全确定所有表达式中的求值顺序。
在C语言中,评估顺序的某些方面未指定,而某些未定义。(这与我理解的Java相反,Java定义了评估顺序的所有方面。)
另请参阅此答案,尽管它涉及的是另一个问题,但会更详细地解释相同的观点。
当一个表达式具有多个运算符时,优先级和关联性很重要。
关联性并不此事与另外,因为你可以从小学数学记得,加法是可交换和关联-有没有什么区别(a + b) + c,a + (b + c)或者(b + c) + a(但看到提示在我的回答结束)。
但是考虑减法。如果你写
100 - 50 - 5
Run Code Online (Sandbox Code Playgroud)
是否将其视为重要
(100 - 50) - 5 = 45
Run Code Online (Sandbox Code Playgroud)
要么
100 - (50 - 5) = 55
Run Code Online (Sandbox Code Playgroud)
左关联性意味着将使用第一种解释。
当您有不同的运算符时,例如,优先级起作用
10 * 20 + 5
Run Code Online (Sandbox Code Playgroud)
由于*优先级高于+,因此将其视为
(10 * 20) + 5 = 205
Run Code Online (Sandbox Code Playgroud)
而不是
10 * (20 + 5) = 250
Run Code Online (Sandbox Code Playgroud)
最后,仅当子表达式之间存在副作用或其他依赖性时,评估顺序才明显。如果你写
x = f() - g() - h()
Run Code Online (Sandbox Code Playgroud)
而且这些函数都可以打印某些内容,而该语言并没有指定输出发生的顺序。关联性不会改变这一点。即使以从左到右的顺序减去结果,也可以以不同的顺序调用它们,将结果保存在某个地方,然后以正确的顺序减去它们。例如,它可能就像您写的那样:
temp_h = h();
temp_f = f();
temp_g = g();
x = (temp_f - temp_g) - temp_h;
Run Code Online (Sandbox Code Playgroud)
前3行的任何重新排序都可以作为解释。
请注意,在某些情况下,计算机算术与实际算术并不完全相同。计算机中的数字通常具有有限的范围或精度,因此可能会出现异常结果(例如,如果相加的结果太大,则会溢出)。即使使用理论上相关联的运算符,这也可能导致取决于运算顺序的不同结果,例如,数学上以下两个表达式是等效的:
x + y - z = (x + y) - z
y - z + x = (y - z) + x
Run Code Online (Sandbox Code Playgroud)
但是,如果x + y溢出,结果可能会有所不同。如有必要,可使用显式括号覆盖默认的关联性,以避免出现此类问题。