括号总是被认为是一个函数调用吗?

5

6
不,括号在编程中同样被用于强制运算符优先级,就像在简单的数学中所做的那样。 - Thomas Jager
2
x = a * (b+c)-(d*e) 是一个表达式,而不是语句。在这里,( 不是一个运算符。术语“括号运算符”是指 () 作为运算符的情况。同样,在 int *p = 0; 中,*= 不是运算符,即使相同的符号在不同的上下文中用作运算符。 - M.M
@SouravGhosh 那么普通括号不算运算符? - klutt
@Broman 不,它们不是。 - Sourav Ghosh
3
@Lundin 真的吗?我发现这是一本很好的参考书,可以帮助普通读者理解运算符优先级,而无需深入理解标准语法。 - dbush
显示剩余13条评论
3个回答

6
括号可以用作函数调用运算符,但这并不是它们唯一的用途。它们还可用于表达式分组,就像您的示例中一样。
您要查找的内容在C标准的第6.5.1节中,该节讨论主表达式。

Syntax

1

primary-expression:
  identifier
  constant
  string-literal
  ( expression )
  generic-selection

...

5 A parenthesized expression is a primary expression. Its type and value are identical to those of the unparenthesized expression. It is an lvalue, a function designator, or a void expression if the unparenthesized expression is, respectively, an lvalue, a function designator, or a void expression.

如上所述,括号可以用来分组表达式。
在后缀表达式的第6.5.2节中详细介绍了作为函数调用运算符的使用方法:
postfix-expression:
  ...
  postfix-expression(argument-expression-list opt)
  ...

所以在你的表达式中:

x = a * (b+c)-(d*e)

这里使用括号匹配主表达式而不是后缀表达式。除了表达式分组外,语言语法的其他部分也使用括号。第6.8.4节关于选择语句在ifswitch语句的语法中使用括号:if (expression) statement, if (expression) statement else statement, switch (expression) statement。第6.8.5节关于迭代语句也在whilefor语句的语法中使用括号。
  • while (表达式) 语句
  • do 语句 while (表达式);
  • for (表达式可选; 表达式可选; 表达式可选) 语句
  • for (声明 表达式可选; 表达式可选 ) 语句

你能否添加一些关于括号不总是被视为运算符的内容。这似乎是我的一个误解。 - klutt

4

函数调用是后缀表达式。

在这些表达式中:

x = a * (b+c)-(d*e);

子表达式(b+c)(d*e)是主要表达式。您可以将任何表达式括在括号中,这样您就会得到一个主要表达式。

例如,您甚至可以以以下方式重写表达式语句

( x = ( ( ( a ) * (b+c) )-(d*e) ) );

在这个表达式语句中,有以下的基本表达式。
( a )
(b+c)
(d*e)
( ( a ) * (b+c) )
( ( ( a ) * (b+c) )-(d*e) )
( x = ( ( ( a ) * (b+c) )-(d*e) ) )

以下是后缀表达式的一些示例。
( *p )() // a function call

a[n] // using the subscript operator

x++; // using the postfix increment operator

函数调用的定义是:

postfix-expression:
    primary-expression
    postfix-expression ( argument-expression-listopt )

从C标准(6.5.2.2函数调用)中可知:

1. 表示被调用函数的表达式必须具有指向返回void或返回非数组完整对象类型的函数指针类型。

下面是一些奇怪的函数调用的例子:)

#include <stdio.h>

void f( void )
{
    printf( "Hello " );
}

void g( void )
{
    puts( "Broman" );
}    

int main( void )
{
    void ( *funcs[] )( void ) = { f, g };

    void ( **p_func )( void ) = funcs;

    ( *p_func++ )();
    p_func[0]();
}

程序输出为:
Hello Broman

请注意,在这些调用中

    ( *p_func++ )();
    p_func[0]();

( *p_func++ ) 是一种基本表达式(Primary Expression),而表达式 p_func[0] 则是后缀表达式(Postfix Expression)(参见上文对后缀表达式的部分定义)。


(*p)()(*p)都是后缀表达式,对于新手来说可能会有些困惑。当谈论函数调用时,“后缀表达式”一词通常指的是函数调用运算符之前的表达式。 - M.M

2

没有标识符时(将标识符作为函数调用。如果括号左边没有标识符或完整表达式,则没有调用。

当我第一次学习c语言时,我遇到了相反的问题。我弄不清为什么clrscr;不能清除屏幕。(它是一个求值为指向clrscr的指针的表达式,但没有任何作用)。

实际上,您可以拥有函数指针类型的表达式,并且这些表达式可以使用()进行调用,语法在两者之间完全没有歧义。因此,clrscr();是函数调用,(clrscr)()也是函数调用。在到达函数指针时,我们也可以执行resolve_function()()。操作始终是在表达式后面的(,而不是在运算符后面。如果是在运算符后面,它必须是分组括号。

"最初的回答"翻译成英文是"Original Answer"


函数调用的语法是“后缀表达式 ( 参数表达式列表)”,其中 参数表达式列表 是可选的。后缀表达式 不需要是标识符。它可以是指向函数的指针,甚至是返回指向函数的指针的函数调用。 - Eric Postpischil
@EricPostpischil:你说得对。我猜测了一下OP的水平,然后写了相应的内容。 - Joshua
您可以编辑您的回答。我经常面临一个问题,即同时想要向新手展示完整的真相,而且有各种方法。您可以写“标识符参数)”始终是函数调用,并且在它们之前没有立即表达式的括号是括号表达式或者类型转换(而不是函数调用),然后稍后您还可以添加“后缀表达式参数)”也是函数调用,这与函数指针和函数名称一起使用。 - Eric Postpischil
另外值得一提的是,C语言文法是对C源代码结构的正式描述,这使得我们总能够将它们区分开来。 - Eric Postpischil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接