C语言中函数调用算子的结合性
我正在讨论C操作符的结合性 在那里,我遇到了这样一个事实:函数调用操作符C语言中函数调用算子的结合性,c,associativity,C,Associativity,我正在讨论C操作符的结合性 在那里,我遇到了这样一个事实:函数调用操作符()具有从左到右的关联性。但只有当一个表达式中出现多个具有相同优先级的运算符时,关联性才会发挥作用。但我找不到任何涉及函数调用操作符的例子,其中关联性起着至关重要的作用 例如在语句a=f(x)+g(x)中,结果取决于求值顺序,而不是两个函数调用的关联性。 类似地,调用f(g(x))将首先计算函数g(),然后计算函数f()。这里我们有一个嵌套函数调用,关联性也不起任何作用 此优先级组中的其他C运算符是数组下标[],后缀+和后缀
()
具有从左到右的关联性。但只有当一个表达式中出现多个具有相同优先级的运算符时,关联性才会发挥作用。但我找不到任何涉及函数调用操作符的例子,其中关联性起着至关重要的作用
例如在语句a=f(x)+g(x)中代码>,结果取决于求值顺序,而不是两个函数调用的关联性。
类似地,调用f(g(x))
将首先计算函数g()
,然后计算函数f()
。这里我们有一个嵌套函数调用,关联性也不起任何作用
此优先级组中的其他C运算符是数组下标[]
,后缀+
和后缀--
。但我找不到任何涉及这些运算符与()
组合的示例,其中关联性在表达式求值中起作用
所以我的问题是,从左到右定义函数调用的关联性是否会影响C中的任何表达式?任何人都可以提供一个例子,说明函数调用运算符的关联性在表达式求值中起作用吗?下面是一个例子,其中函数调用运算符的左右关联性起作用:
#include <stdio.h>
void foo(void)
{
puts("foo");
}
void (*bar(void))(void) // bar is a function that returns a pointer to a function
{
puts("bar");
return foo;
}
int main(void)
{
bar()();
return 0;
}
相当于:
(bar())();
除了@grzegorzzpetkowski的答案外,您还可以获得以下内容:
void foo(void) { }
int main(void) {
void (*p[1])(void);
p[0] = foo;
p[0]();
return 0;
}
这将创建一个函数指针数组,因此可以将数组下标运算符与函数调用运算符一起使用。函数应用程序与左侧关联的语句在C语言定义中是完全冗余的。函数参数列表显示在函数表达式本身的右侧,并且必须用括号括起来,这一事实暗示了这种“关联性”。这意味着,对于给定的函数表达式,其参数列表的范围是不容置疑的:它从函数表达式后面的强制开括号扩展到匹配的右括号
函数表达式(通常只是一个标识符,但可能更复杂)本身可能是一个(裸)函数调用,如f(n)(1,0)
。(当然,这需要由代码> f(n)< />代码返回的值,然后可以用参数列表<代码>(1,0)< /> >调用,C具有一些可能性和C++更多,但是这些是在解析之后才起作用的语义考虑,因此它们应该被忽略用于关联性讨论。这意味着函数调用的语法规则是左递归的(函数部分本身可以是函数调用);这可以用“函数调用向左关联”来表述,但事实是显而易见的。相比之下,右部分(参数列表)不能是(裸)函数调用,因为需要括号,所以不能有右递归
例如考虑<代码>(a)(b)(c)< /代码>(在这里我放了多余括号,以表示对称性和可能的歧义)。这里的
(b)(c)
本身可能被认为是b
(冗余括号)的调用,带有参数c
;但是,这样的调用不能解释为a
的参数,因为这需要额外的括号,如(a)((b)(c))
。因此,在没有提到关联性的情况下,(a)(b)(c)
显然只能意味着a
是通过参数b
调用的,结果值是通过参数c
调用的,如果f
是一个表达式本身(如从数组中选择的一个函数指针),该怎么办?任何返回函数指针的函数都可以用作示例,因为函数调用运算符不需要函数指针解引用。因此,你开始使用类似于f()
的东西,它是有效的C。。。。它必须是从左到右的。你愿意生活在这样一个世界里吗?f(a)(b)
是对f
的一个调用,通过b
然后用a
调用结果(也就是说(f(b))(a)
)-你愿意吗?我不同意现有的答案,不管投票结果如何。他们确实很好地解释了一些基本原理,但却深入了很多。是的,关于C语法区分“主”和“一元”运算符,我们可能有很长的争论,但这种区分也只是语法的产物。非常感谢。在处理所有前缀或后缀的一元运算符时,关联性在什么意义上相关?我认为它主要适用于前缀和后缀运算符都应用于同一项的情况,如*foo()
。我想不出任何关于foo
的定义会允许(*foo)(
和*(foo())
在C语言中是合法的(这样的事情在C++中可能存在),但事实是,在某些情况下,每种形式都是合法的,只有后者可以用*foo()替换
将证明*
和()
@GrzegorzSzpetkowski的相对关联性:在我看来,如果它们真的是运算符,那么就有可能写出类似foo->(expr?bar:boz)
的东西,并且其意义等同于expr?foo->bar:foo->boz
(事实上,想想看,使用C语言可以使某些代码更清晰,并且不会使任何当前的法律结构变得模棱两可;但是,这将严重违反“无新发明”规则)。尽管如此,我认为说二进制运算符连接表达式更有用,因此
和->
不是二进制运算符,而不是考虑这些东西…@super
void foo(void) { }
int main(void) {
void (*p[1])(void);
p[0] = foo;
p[0]();
return 0;
}