当前位置: 首页 > 知识库问答 >
问题:

除了运算符优先级外,外圆括号什么时候起作用?

秋煌
2023-03-14

C中的括号在许多地方使用:例如,在函数调用和分组表达式中以覆盖运算符优先级。除了非法的额外括号(例如在函数调用参数列表周围)之外,C的一般(但不是绝对)规则是额外的括号永远不会受到伤害:

5.1主表达式

5.1.1概述[初步概述]

6括号表达式是其类型和值与封闭表达式相同的主表达式。括号的存在不影响表达式是否为左值。括号表达式可以在与可以使用封闭表达式的上下文完全相同的上下文中使用,并且具有相同的含义,除非另有说明。

问题:除了覆盖基本运算符优先级外,在哪些上下文中,外圆括号会改变C程序的含义?

注意:我认为将指针指向成员的语法限制为


共有2个答案

齐夕
2023-03-14

一般来说,在编程语言中,“额外”括号意味着它们不会改变语法分析顺序或含义。添加它们是为了澄清顺序(运算符优先级),以便于人们阅读代码,它们的唯一效果是略微减慢编译过程,并减少理解代码时的人为错误(可能会加快整个开发过程)。

如果一组括号实际上改变了一个表达式的解析方式,那么根据定义它们不是多余的。将非法/无效解析转换为合法解析的括号不是“多余的”,尽管这可能指出了糟糕的语言设计。

应俊爽
2023-03-14
匿名用户

在以下上下文中,多余的括号会更改 C 程序的含义:

    < li >防止依赖于参数的名称查找 < li >在列表上下文中启用逗号运算符 < li >令人烦恼的语法分析的歧义消解 < li >推导< code>decltype表达式中的引用关系 < li >防止预处理器宏错误

如本标准附录A所述,格式为(表达式)后缀表达式是主表达式,但不是id表达式 ,因此也不是。这意味着与传统的fun(arg)形式相比,在(fun)(arg)格式的函数调用中可以防止参数相关的名称查找。

3.4.2参数相关名称查找[basic.lookup.argdep]

1当函数调用(5.2.2)中的postfix-表达式是一个不合格的id时,可以搜索在通常的不合格查找(3.4.1)期间未考虑的其他命名空间,并且在这些命名空间中,可以找到命名空间范围的朋友函数或函数模板声明(11.3),否则不可见。这些对搜索的修改取决于参数的类型(对于模板模板参数,模板参数的命名空间)。[示例:

namespace N {
    struct S { };
    void f(S);
}

void g() {
    N::S s;
    f(s);   // OK: calls N::f
    (f)(s); // error: N::f not considered; parentheses
            // prevent argument-dependent lookup
}

-结束示例]

逗号运算符在大多数类似列表的上下文(函数和模板参数、初始值设定项列表等)中具有特殊含义。在这种情况下,a,(b,c),d形式的括号可以启用逗号运算符,而普通形式的a,b,c,d则不适用逗号运算符。

5.18 逗号运算符 [expr.comma]

2在逗号具有特殊含义的上下文中,[示例:在函数参数列表(5.2.2)和初始化程序列表(8.5)-结束示例中]第5条中描述的逗号运算符只能出现在括号中。[示例:

f(a, (t=3, t+2), c);

有三个参数,第二个参数的值为5。—示例结束]

向后兼容C语言及其晦涩难懂的函数声明语法可能会导致令人惊讶的解析歧义,称为令人烦恼的解析。本质上,任何可以被解析为声明的东西都将被解析为声明,即使竞争解析也适用。

6.8 歧义解析

1 涉及表达式语句和声明的语法中存在歧义:具有函数样式显式类型转换 (5.2.3) 作为其最左侧子表达式的表达式语句可能与第一个声明以 a 开头的声明无区别 (.在这些情况下,该语句是声明。

8.2 歧义解析 [dcl.ambig.res]

1由于6.8中提到的函数式造型和声明之间的相似性而产生的歧义也可能出现在声明的上下文中。在这种情况下,需要在一个参数名周围有一组多余括号的函数声明和一个以函数样式强制转换为初始值设定项的对象声明之间做出选择。就像6.8中提到的歧义一样,解决方案是将任何可能是声明的构造都视为声明。[注意:可以通过非函数式强制转换、通过=来指示初始化或者通过移除参数名周围多余的括号来显式消除声明的歧义。—结束注释] [示例:

struct S {
    S(int);
};

void foo(double a) {
    S w(int(a));  // function declaration
    S x(int());   // function declaration
    S y((int)a);  // object declaration
    S z = int(a); // object declaration
}

-结束示例]

一个著名的例子是最令人烦恼的解析,这个名字由Scott Meyers在他的有效STL书的第6项中推广:

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
               istream_iterator<int>());        // what you think it does

这声明了一个函数< code>data,它的返回类型是< code>list

  • 第一个参数名为dataFile。它的类型是istream_iterator

在第一个函数参数周围放置额外的括号(第二个参数周围的括号是非法的)将解决歧义

list<int> data((istream_iterator<int>(dataFile)), // note new parens
                istream_iterator<int>());          // around first argument
                                                  // to list's constructor

C 11具有大括号初始化器语法,允许在许多上下文中绕过此类解析问题。

自动类型推导相反,decltype允许推导引用性(左值引用和右值引用)。这些规则区分了decltype(e)de cltype((e))

7.1.6.2 简单类型说明符 [dcl.type.simple]

4对于表达式e,由dectype(e)表示的类型定义如下:

— 如果 e 是未加修饰的 id 表达式或未加修饰的类成员访问 (5.2.5),则 decltype(e) 是由 e 命名的实体的类型。如果没有这样的实体,或者如果e命名了一组重载函数,则程序是病态的;

-否则,如果e是一个x值,decltype(e),则为T

-否则,如果e是左值,dectype(e)T

— 否则,decltype(e)e 的类型。

dectype说明符的操作数是未计算的操作数(第5条)。[示例:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0;   // type is const int&&
decltype(i) x2;           // type is int
decltype(a->x) x3;        // type is double
decltype((a->x)) x4 = x3; // type is const double&

—end example ] [注:确定涉及< code>decltype(auto)的类型的规则是在7.1.6.4指定的。—结束注释]

对于初始化表达式的RHS中的额外括号,dectype(auto)的规则具有类似的含义。这是C常见问题解答中的一个示例和此相关问题

decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }  //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B

第一个返回< code >字符串,第二个返回< code >字符串

预处理器宏在与C语言本身的交互中有许多微妙之处,其中最常见的如下所示

    < li >在宏定义< code>#define TIMES(A,B) (A) * (B)内的宏参数周围使用括号;以避免不必要的运算符优先级(例如,在< code > TIMES(1 ^ 2,2 ^ 1)中,将产生9,但如果< code>(A)和< code>(B)两边没有括号,将产生6 < li >在包含逗号的宏参数周围使用括号:< code>assert((std::is_same

 类似资料:
  • 问题内容: 在Java教程优先级表中,plus运算符的优先级仅列出一次。但是以下Java表达式: 结果如下: 这是否意味着加号运算符在用于连接字符串时具有较高的优先级,或者是说加号运算符的优先级对于字符串和数字没有什么不同,只是简单地从左到右求值? 问题答案: 总是 从左到右 流动 __ 在您的第一个示例中,string首先出现,然后是int值,因此1被视为string。 在第二个示例中,您使用的

  • 圆括号运算符用于改变其他运算符的优先级,例如表达式${4 + 5 * 2},先计算5 和2的乘积,再将计算结果和4相加。如果该表达式改成${(4 + 5) * 2},EL引擎会先计算4和5的和,然后将计算结果和2相乘,并输出最终的计算结果。

  • 从高到低顺序如下: ^ not - (一元运算) * / + - ..(字符串连接) < > <= >= ~= == and or

  • 问题内容: 我看了这个关于表达式(以下简称“逗号操作”的问题)和MDN文档有关,但我想不出一个场景的地方是有用的。 那么,逗号运算符什么时候有用? 问题答案: 以下内容可能不是很有用,因为您不是自己编写的,但是压缩程序可以使用逗号运算符来缩小代码。例如: 会成为: 现在可以使用该运算符,因为逗号运算符(在一定程度上)允许将两个语句写为一个语句。 这 是 有用的,因为它允许一些整齐压缩(39 -这里

  • 通过前面的学习我们知道运算符有不同的类型,当这些不同类型的运算符出现在同一个表达式中时,就必须遵循运算符的优先级来进行运算,才能保证运算的合理性和结果的正确性、唯一性。运算符的优先级决定了表达式中各个部分执行运算的先后顺序。 下表中详细介绍了 C# 中运算符的优先级顺序: 优先级 运算符 名称或含义 使用形式 结合方向 说明 1 [] 数组下标 数组名[整型表达式] 左到右   () 圆括号 (表

  • 运算符的优先级决定了不同的运算符在表达式中计算的先后顺序,下表列出了 MySQL 中的各类运算符及其优先级。 优先级由低到高排列 运算符 1 =(赋值运算)、:= 2 II、OR 3 XOR 4 &&、AND 5 NOT 6 BETWEEN、CASE、WHEN、THEN、ELSE 7 =(比较运算)、<=>、>=、>、<=、<、<>、!=、 IS、LIKE、REGEXP、IN 8 | 9 & 10