为什么在大括号后面不需要分号?

13

我知道在语句结束处需要加分号(我指的是Java、C++和类似的语言),但是在花括号结束后不需要。为什么呢?

if (a > b) 
  printf("hello!"); // semicolon is mandatory

if (a > b) {
  printf("hello!");
} // semicolon is not required

什么是原因?我是指,背后的理论是什么?


Kaivosukeltaja的句子是什么意思?我无法理解。 - irreputable
令人沮丧的是,在IE9中需要它们,否则你会遇到奇怪的无意义错误,比如“数组未定义”或“无效指针”。 - Cerin
12个回答

8

由于该语言定义如下:

statement:
    labeled-statement
    expression-statement
    compound-statement
    selection-statement
    iteration-statement
    jump-statement
    declaration-statement
    try-block

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

expression-statement:
    expressionopt ; 

compound-statement:
    { statement-seqopt } 

statement-seq:
    statement
    statement-seq statement

selection-statement:
    if ( condition ) statement
    if ( condition ) statement else statement
    switch ( condition ) statement

condition:
    expression
    type-specifier-seq declarator = assignment-expression

iteration-statement:
    while ( condition ) statement
    do statement while ( expression ) ; 
    for ( for-init-statement conditionopt ; expressionopt ) statement

for-init-statement:
    expression-statement
    simple-declaration

jump-statement:
    break ;
    continue ;
    return expressionopt ; 
    goto identifier ; 

declaration-statement:
    block-declaration 

所有正常的控制语句都是相互递归构建的。真正的工作由expression-statement完成。如果您注意到expression-statement总是以;结束。其他需要注意的语句是jump-statement

但主要原因是它们在{}块后面不需要,以便easy解析语句。


4
我认为它不会使解析更容易或更困难。我怀疑不需要在复合语句后使用它们的主要原因是因为否则很容易确定复合语句的结束。(就编译器而言,goto也适用于相同的逻辑,但人类读者可能会容易混淆。) - James Kanze
你提到break/continue/goto是多余的,只是为了让用户感觉一致。但我很想花时间用bison分析C++语法,在表达式语句末尾加上或不加';',看看会产生多少冲突。 - Martin York
@James:使用这个之后,去掉了init_declaration(s)后面的分号,将0个移位/规约冲突增加到了288个。 (在此语法文件中,表达式语句已被init_declarations(s)替换(文件中的注释解释了详细信息)) - Martin York
声明后面的分号是必需的,因为在声明中的}并不一定表示结束。而对于复合语句来说,情况就不同了。我能想到的只有复合语句、goto语句、break语句和continue语句是没有歧义的结束符号,不需要加上分号。我猜测语言设计者希望每个语句都以一个标点符号结尾,所以只省略了复合语句中的分号。 - James Kanze
@James:感谢你提醒,我已经小心地删除了分号。我的原始语句仍然有效。在 expression statement 后面没有分号会使解析变得更加困难。 - Martin York

8

由于花括号用于分组语句,但它们本身不是语句。


1
如果是这样,那么为什么在类定义的花括号后需要分号呢? - Nawaz
7
@Nawaz,在类定义后面是不需要使用分号的。但在可能为空的变量定义列表之后需要使用分号,这个规则要注意哦 :-) - Tadeusz Kopec for Ukraine
9
答案不正确。根据C++标准,{ _statement-seq_[opt] }是一个复合语句;一种语句类型。他们不要求使用 ; 是因为语法不要求,纯粹简单如此。 - James Kanze
1
@Kaivosukeltaja 但是我所知道的所有其他语言都使用相同的基本规则。他们定义了一个复合语句生成器。那些使用;终止符号和{ ... }表示复合语句的语言在复合语句的生成中都省略了;终止符号(可能是因为}不会产生歧义地作为语句的结束)。 - James Kanze
1
复合语句可以出现在任何需要语句的地方。因此,它们也是语句。 - SK-logic
显示剩余8条评论

5

除了哲学推理之外,在引擎盖下,编译器知道在哪里将每个命令与下一个命令分开是至关重要的。括号本身就是分隔符,所以分号是不必要的。


只是小问题,在C++中,};不是分隔符,而是终止符。但你的基本观点是正确的:分号不必要的原因是}能够明确告诉编译器复合语句何时结束。 - James Kanze

3
大多数人认为语句是一种简单的命令,通常带有关键字和一些参数,例如“goto x”,“a=y+2”等。必须有某种指示表明一个语句何时结束,另一个语句何时开始,就像英语句子需要以句号结束一样。传统上,大多数语言的语法要求在这种语句之后加上分号作为这种指示。
A { ... } “花括号对”是一个块,是一种特殊的语句,但不需要分号,因为花括号使边界清晰明了。
许多语言还允许使用“;”表示空语句。你为什么需要一个空语句?和自然数系统需要“零”而不是“一”以及集合可以为空的原因相同。
但这意味着你可以写:
{ ... } ;

大多数语言编译器都能接受它而不做任何评论。但是你应该把它看作:

{  ... }
;

通常情况下,没有好的理由去编写这种代码。

实际上,接受{}(例如,“空括号”)的语言不需要空语句;,因为它们在语义上是相同的。但是语言设计者似乎被传统所束缚;你是否注意到每个“现代”语言似乎都是C语法的糟糕副本?


2
一个“块”是一条语句,根据C++标准。 (但我知道所有其他具有块的语言也是如此。) - James Kanze
@James Kanze:我的回答旨在吸引程序员的直觉(他们中很少有人会将代码块称为语句)。从技术上讲,语法必须将“代码块”集成到“语句”的类别中,以允许在直观意义下允许语句的地方使用代码块。我对答案进行了轻微修改。 - Ira Baxter
我不确定你所说的“程序员的直觉”是什么意思。我认为大多数程序员会按照他们所学到的方式来考虑一个代码块。我无法想象将其教授为其他形式而不是复合语句。 - James Kanze
@James Kanze:观察一下提问者的问题,并注意它所获得的“好问题”标志的数量。显然,他/她并没有你所教授的理解能力。 - Ira Baxter

1
这是一个公平的问题。块是一种语句。自然而然地希望一致性,并想知道为什么不所有语句都终止相同。如果我们要求在块后添加 ;,那么就没有技术问题。但我们也很懒,因为 } 可以明确标记语句的结尾,所以我们不想再键入另一个标记。
相关观察:在 C++ 中,您必须使用 ; 结束类声明。
class A
{
    ...
}; // the semicolon is mandatory!

这让很多人感到非常烦恼。分号是必需的,因为语言允许在 } 之后添加其他内容,所以 } 不是可靠的结束标记。

在Java中,情况并非如此。 } 结束类声明,就是这样。因此不需要 ;


0
在编程中,加上分号的效果与

相同。
if (a > b) {
 printf("hello!");
}printf("Goodbye");

并且省略掉 printf("Goodbye") 部分。


0

在关闭大括号后唯一需要使用分号的地方是在数组初始化之后,因为您可以继续该行,例如:

int[] i= { 1,2,3}, j= {1};

分号是必需的,因为这里的 '}' 无法告诉编译器该行代码的结束位置。
同样地,
Runnable r = new Runnable() {
   public void run() {
   }
}, r2 = new Runnable() {
   public void run() {
   }
}; // required as you could define another Runnable or an array of Runnable.

0
如果您正在使用C# 3+中的对象初始化语法,则在括号后面加上分号。
var foo = new Foo
  {
    Bar = "Fizzbuzz"
  };

0
在这种情况下,花括号定义了一组语句,就像任何其他的代码块一样。当你要声明和初始化数组时,必须提供分号,因为在这种情况下你正在编写一个语句。

0

注意:此答案特指C++语言,不适用于Java。

我认为分号在语言语法中并非必需(2003年)。这是语言如何定义语法的。

您编写的代码称为复合语句或块,语言规范(2003年)在第§6.3/1节中定义了复合语句的语法:

提供复合语句(也可以等效地称为“块”)以便可以使用多个语句来替代单个语句。

复合语句:
             { statement-seqopt }
语句序列: 语句 语句序列 语句

你在上面展示的语法中看到任何分号了吗?没有。因此,在您的代码中左花括号后面不需要分号。


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