为啥 C 语言在 if 语句中需要围绕简单条件的括号?

Posted

技术标签:

【中文标题】为啥 C 语言在 if 语句中需要围绕简单条件的括号?【英文标题】:Why do C languages require parens around a simple condition in an if statement?为什么 C 语言在 if 语句中需要围绕简单条件的括号? 【发布时间】:2011-01-04 22:02:12 【问题描述】:

这听起来很愚蠢,但多年来我一直无法想出一个需要这个的用例。快速的谷歌搜索没有发现任何有价值的东西。

根据记忆,Bjarne Stroustrup 提到了一个用例,但我找不到对它的引用。

那么为什么你不能在 C 语言中使用它:

int val = 0;
if val
  doSomehing();
else
  doSomehinglse();

我可以接受“我们不会为增加对词法分析器的支持而烦恼”的理由,我只是想弄清楚这种语法是否会破坏其他语言结构。考虑到 C/C++ 中有多少古怪的语法特性,我几乎不认为这会增加很多复杂性。

【问题讨论】:

因为语言是这样定义的? 我认为它可能会成为一堆嵌套 if / else 块的问题。 好问题。而cx0der:为什么是这样定义的?许多语言允许您省略括号。在 C 语言家族中是否存在实际上将歧义引入解析器的情况?我想不出来。 @cx0der:许多编译语言确实允许这样做。解释与编译与它无关。问题在于语言的语法,而不是代码的执行方式它已被解析 其他一些语言使用'then'来标记条件的结束;关键字就像括号一样。 Dennis Ritchie 选择不使用关键字将条件与动作分开,而是选择使用括号。有些语言有关键字来介绍所有语句;这样的语言不需要括号,因为关键字再次标记下一条语句的开始,因此条件和动作的分离是明确的。 【参考方案1】:

如果if 构造中的表达式周围没有括号,那么以下语句的含义是什么?

if x * x * b = NULL;

是吗

if (x*x)
    (*b) = NULL;

是吗

if (x)
    (*x) * b = NULL;

(当然,这些都是愚蠢的例子,甚至出于明显的原因都不起作用,但你明白了)

TLDR:C 中需要括号来消除任何语法歧义的可能性。

【讨论】:

… 或 if ( x * x * b = NULL ) ; 在解析器尝试将函数与操作相匹配之前看起来会很好。 这确实不是问题的好答案。 C 严格规定了运算符的操作顺序。如果那是有效的语法,它将被评估为 if(NULL) 因为 * 优先于 = 并且赋值的返回值是所分配的值,并且没有逻辑理由为什么编译器不需要括号括起来条件将在 if 之后拆分语句(查看 python 语法)。 Chris:重点是,LR 解析器需要能够决定条件在哪里结束以及条件块从哪里开始。不同的语言有不同的方法,有些需要括号,有些换行,有些有“then”关键字。你只是无法解决这个问题。向我展示一种 语言,它没有某种机制以某种方式将条件表达式与条件块分开。如果您仍然不相信我,请尝试使用 yacc 构建您自己的语言语法,您会看到所有会出现的问题。 @Chris:即使有一种明确的方法来解析它,它也不会那么明显,而且难以阅读,因此是个坏主意。例如,我总是在嵌套和/或/非条件下放入额外的括号,即使它们不是必需的。节省每个人思考隐含优先级如何发挥作用的时间。【参考方案2】:

告诉我如何解释以下内容:

if x ++ b;

看起来很傻但是...

if( x ) ++b;

if( x++ ) b;

或者也许“x”有一个重载的运算符然后......

if( x ++ b);

编辑:

正如 Martin York 所指出的,“++”具有更高的优先级,这就是我必须添加运算符重载花絮的原因。他在处理现代编译器时是绝对正确的,直到您允许 C++ 带来的所有重载优点。

【讨论】:

后增量具有更高的优先级。所以最后一个。一个更好的是如果 x + b;这是 if (x+b) ; 还是 if (x) +b; 或更常见的识别是if x - b,因为一元减号比一元加号更熟悉。 "interprit" 里面有两个 'e's。【参考方案3】:

我认为更好的问题是“我们为什么想要这样的东西?”而不是“为什么我们没有它?”。它在词法分析器中引入了另一个不必要的边缘情况,这样您就可以避免输入 4 个额外的字符;并通过允许对已经支持所有可能情况的简单语法的例外来增加规范的复杂性。更不用说模棱两可了,这在编程语言中没有多大价值。

【讨论】:

许多语言允许您省略括号,减少噪音总是有助于清晰。假装它是某种无法克服的障碍有点愚蠢。最后一部分更有趣。你能想想它会引入歧义的任何情况吗?否则,您的回答似乎只不过是“括号对我来说总是足够好!不要来这里暗示我的语言不完美” 当然会增加复杂性。编译器必须弄清楚程序员是否给了它一个简单的条件(即布尔值)或更复杂的东西;例如,如果用户有一个语句“if someObj doStuff()”,其中 someObj 有一个巨大的函数可以隐式转换为 bool? 我同意 C++ 不是引入此类功能的正确语言。也就是说,我还认为我们在编写的大部分代码中都没有充分利用我们自然的“语言本能”。恰当的例子:前一句只有两个引号和一个逗号作为标点符号......但我们都在脑海中解析它。我的怀疑是,在许多情况下,编程可以利用潜在的共同理解,这使我们能够做到这一点。最近我一直在研究这个问题,它发人深省。 @Hostile Fork。不必要的样板是不好的,但过多的表达灵活性也可能是不好的,即使它不会引入歧义。你在这里写代码,而不是写诗。没有必要让编译器变得过于复杂(这很容易导致错误),您还必须考虑维护程序员。使用更灵活的语言(例如 Perl),您可以轻松创建难以理解的只写代码。 @jalf - “...降低噪音总是有助于清晰...”显然是不真实的。作为一个简单的反例,您为什么认为有些人会添加完全不必要的括号?因为对于某些人多余的括号有助于清晰!【参考方案4】:

要记住的另一件可能的事情:C 是在磁带存储很常见的时候创建的,因此随机查找或向后浏览当前文件甚至其他文件实际上并不可行(这也解释了为什么你必须将一些东西(即前向声明)放在其他东西(即函数的使用)之前,即使编译器应该能够自己弄清楚)。

【讨论】:

磁带与它无关。编译器设计的重点一直是线性时间内从左到右的处理。许多其他语言都有前向声明规则,而不仅仅是磁带时代的语言。【参考方案5】:

它只是结束语句的条件子句的一种方式。 其他语言使用其他方式,例如“then”或 Python 中的“if condition :(Followed by a return)” ( : 总是让我进入 python 我一直把它们排除在外并得到错误而不是立即明显的结果。)

计算机语法通常喜欢一些独特的机制来识别语句或表达式的不同部分。在这方面,它并不比任何其他语言更好或最差。从语法的角度来看,C/C++ 的 dangle else 问题是一个模棱两可的例子,必须作为特殊情况处理以确保正确操作。 (即,它为编译器实现者增加了额外的工作。)

【讨论】:

【参考方案6】:

我也喜欢这种语法,但为了避免解析歧义,必须采用这种形式:

对于多个语句:

if val 
 
    doSomething();
    doOthers();

对于一个陈述:

if val: 
   doSomething();

【讨论】:

【参考方案7】:

除非您想要大量空白,否则您需要能够判断条件何时结束 在某些语言中,它是 (),在某些语言中,它是关键字,然后是一个 if,就像答案中给出的那样

如果 x * x * b = NULL 可能是一个总是评估为假的条件,一个在 (xx) 的情况下将 null 分配给 b,编译器无法知道条件何时结束并开始以下操作。所以需要有一个明确的“后退标记”让编译器知道条件何时结束。在 C(++)、Java、c# 和其他语言中,它是 () 标记 F# 中的条件,它是关键字“then”,在其他语言中它是换行符/缩进

【讨论】:

【参考方案8】:

根本原因是C和朋友没有'then'关键字,所以他们需要另一种方式来分隔条件表达式。这样做的语言,如 Pascal 和 PL/1,不需要括号。同样适用于“while”,其中 Pascal、PL/1 等有一个“do”。

【讨论】:

实际上,如果编译器现在可以在一个表达式结束和下一个表达式开始的地方,这就足够了。 (但正如 Jopeku 博士的回答所示,事实并非如此。) @Paŭlo Ebermann 当然这就足够了,这就是“then”关键字所提供的。如果没有“then”关键字,您可能会遇到可能的赋值语句,并且需要任意数量的前瞻。从语法的角度来看,C 的 '(' 是多余的,而 ')' 在功能上与 'then' 相同。【参考方案9】:

这是因为它可以帮助程序员让编译器理解表达式(包括一系列类型的运算符,如算术、逻辑、赋值等)需要在哪个序列中求解才能获得所需的输出。

【讨论】:

【参考方案10】:

我至少可以举一个例子,为什么在 C 语言中,条件周围有括号,

例如考虑以下

while(1) do_something();

上面会在无限循环中永远调用 do_something()

需要括号的原因是因为以下内容不明确

while 1 ; do_something(); 这是否意味着,评估 do_something 并且在 while 循环中什么都不做?还是和上面的代码示例意思一样?

我想这说明了为什么在 C 语法中,条件评估周围需要括号,并且通过扩展相同的语法(为了保持一致性),它被扩展到 if 语句。

Discalaimer:我可能完全错了,拥有这个词的原因可能完全不同,但至少我已经展示了一个例子来说明它是如何有用的

【讨论】:

“while 1 do_something();”怎么样?删除括号会通过省略条件来删除表达无限循环的能力,但不会删除表达无限循环的能力。 其实我觉得应该是while(1) do_something()。对于使用 gcc 4.0.1 的我来说,我的代码出现 ')' 标记之前的语法错误 那是真的,但是你的句法规则不应该允许像我上面展示的那样模棱两可的情况,所以要么在条件语句周围强制执行 (),要么在 while/for/if/else 主体周围强制执行 。 C选择做第一个,或者走python路线(即:空白很重要)。从一开始,C 就不关心空格,所以他们必须在强制()或强制之间做出选择,他们选择了第一个。 添加 1 有点让您的示例变得多余,不是吗?我再也看不到任何歧义了。 你可以这样做:while do_something() || 1;【参考方案11】:

我认为您将“括号”与“括号”混淆了

这在 C 中是可能的:

void doSomething()
void doSomethingElse()

void main() 
    int val = 0;
    if( val ) 
        doSomething();
    else
        doSomethingElse();

并非所有“C”语言都强制使用括号,并且将大括号保留为可选。 Go(问题)编程语言完全符合opposite。

在Issue9中写相同句子的正确方法是:

if val 
    doSomething();
 else 
    doSomethingElse();

这样,以下内容无效:

if x * x * b = NULL; 

它应该是以下之一:

if x*x 
    *b=NULL;

或 如果 x xb=NULL;

这里的大括号消除了由于缺少括号而造成的歧义。

【讨论】:

太好了,这只是重申了它是大括号或括号的概念。 ....或者在 Python 中(我知道它不是来自“C”系列,而是)使用冒号分隔句子。可能是:if x * x : b = None【参考方案12】:

我想不出省略括号实际上会导致任何歧义的任何具体情况,但某处可能存在极端情况。

它可能与本地声明的变量有关(如在if (bool b = foo())... 中,如果没有括号,则很难确定bool 是类型声明还是整个条件。在后一种情况下,@987654323 @ 将是 if 语句主体的第一个标记,而不是条件的一部分。

【讨论】:

除了允许将变量定义作为表达式的一部分不是原始 C 语言的一部分。 @R Samuel:确实给出的示例不适用于 C【参考方案13】:

因为 C 允许人们这样做:

if (x)  // As opposed to java where you need to do if (x == something)
   // your code

正如您在上面看到的那样,灵活性会引起歧义,正如其他人所表明的那样。

【讨论】:

如果 'x' 是布尔值,你可以用 Java 写同样的东西,Java 需要括号,就像 C 代码一样。不以任何方式回答问题。

以上是关于为啥 C 语言在 if 语句中需要围绕简单条件的括号?的主要内容,如果未能解决你的问题,请参考以下文章

在C语言中条件语句都有哪些需要注意的事项?

c语言选择排序中为啥一层for循环中要定义变量k,直接把交换那一步写到上面if语句中不行吗?

c语言中连续3个if语句 三种情况,大于等于小于0,为啥只执行第一个if就不执行下面的了?请问

C语言问题:标识符为啥不能使用关键字?

子数涵数·C语言——条件语句

为啥JAVA源码中if else大都省略else,怎样的条件能够省去else?