C 中的 if 语句在语法上如何明确?

How are if statements in C syntactically unambiguous?

我不太了解 C,但我了解基础知识,据我所知:

int main() {
  if (1 == 1) printf("Hello World!\n");
  return 0;
}

int main() {
  if (1 == 1) 
    printf("Hello World!\n");
  return 0;
}

int main() {
  if (1 == 1) {
    printf("Hello World!\n");
  }
  return 0;
}

在语法上完全相同。该陈述是真实的;打印字符串;大括号(显然)是可选的。

有时,尤其是在 SO 上,我会看到如下内容:

int main() {
  if (1 == 1)
    printf("one is one\n");
  printf("is this inside the if statement??/who kn0WS\n");
  return 0;
}

由于 CodeGolf 赋予的权力,我被引导相信 C 是空白不可知论者;词法分析器将标记分解成它们的组成部分并去除字符串外的空白。

(我的意思是,在每个语句上使用分号的全部原因是解析器可以去除 \n\t 文字空格并且仍然知道每个语句在哪里结束,对吧??)

那么,如果要忽略空格,怎么可能明确地解析前面的代码(或者也许有人可以想出一个更好的例子来说明我的意思)?

如果 C 程序员想用依赖于空白的 Pythonic 语法编写,他们为什么要编写 C,以及为什么在教导 C 的任何地方都教导可以编写词法歧义(对我,一个程序员和计算机)这样的语句?

if (1 == 1)
  printf("one is one\n");
printf("is this inside the if statement??/who kn0WS\n");

第二个 printf() 不应在 内部 if 语句中执行。

原因是上一行以分号结尾,表示if-block执行结束

(I mean, the whole reason for the semicolons-on-every-statement-thing is so the parser can strip \n, \t, literal spaces and still know where each statement ends, right??)

So how is it possible to unambiguously parse the previous bit of code (or perhaps someone can come up with a better example of what I mean), if whitespace is to be disregarded?

解析示例:

if (1 == 1) // if - () - 语句 (或块)跟随,跳过所有空格

// 没有找到 { -> 单个语句,扫描直到 ;(引号/注释外)

printf("one is one\n"); //遇到;,if-block

结束

没有大括号,只有一个语句属于if-block.

但是,如前所述,使用牙套是个好习惯。如果您稍后添加一条语句(例如快速临时 printf()),它将始终在块内。

特例:

int i = 0;
while(i++ < 10);
    printf("%d", i);

这里printf()只会执行一次。在while().

末尾标记;

如果是空语句,最好使用:

while(i++ < 10)
    ;

明确意图(或者,也可以使用空块 {})。

没有大括号,它只是 if 之后的下一个语句。空格无关紧要

始终使用牙套是一种很好的做法,可以让生活更轻松。良好的压痕。这样代码就很容易阅读,并且当人们在 if

之后的 add/remove 语句时不会导致错误

tl;dr 唯一的歧义在于人类阅读的难度。从编译器的角度来看,语法完全没有歧义。

if 语句之后只有两种(可编译且语法上可接受的)可能性:

  1. 大括号,如

    if(x) {
        DoFoo();
    }
    // or
    if(x) { DoFoo(); }
    

    在这种情况下,如果满足条件,{...}中的任何内容都会执行。

  2. 没有大括号,如

    if(x)
        DoFoo();
    // or
    if(x) DoFoo();
    

    在这种情况下,如果条件满足,只有下一条语句会被执行。

你说 C 是 whitespace-agnostic 是正确的。因此,省略大括号会导致一些棘手的错误。例如这段代码中,DoBar()无论是否满足条件都会执行:

if(x)
    DoFoo();
    DoBar();

大括号的不一致使用也很容易导致无效代码。例如,从人类的角度来看,这看起来是有效的(乍一看),但事实并非如此:

if(x)
    DoFoo();
    DoBar();
else
    DoBaz();

None 您发布的示例从编译器的角度来看是模棱两可的,但是 no-braces 版本从人类的角度来看令人困惑。省略大括号经常会导致 hard-to-find 个错误。

可读性是语句中唯一的歧义:

if (1 == 1)
  printf("one is one\n");
printf("is this inside the if statement??/who kn0WS\n");

if(...) 语句之后的第一个语句唯一应该执行的时间是它是否被评估为 TRUE。

大括号,{...} 有助于消除可读性歧义,

if (1 == 1)
{
     printf("one is one\n");
}
printf("is this inside the if statement??/who kn0WS\n");

但语法规则仍然相同。

意见不一,但我总是选择使用大括号。
在编写代码时不使用它们是可以的。但是在路上,您只知道有人会出现并在第一个语句下添加另一个语句并期望它被执行。

在 C 语言中,if 语句在真值表达式之后恰好采用语句,而不管缩进。通常为了清楚起见,此语句缩进,但 C 忽略缩进。无论如何,您的任何示例都没有歧义。

在 C 和许多其他语言中,有歧义的是 "dangling else"。例如,假设您有一个嵌套的 if 语句,在第二个语句之后有一个 else。它可以分组为:

if (expr)
    if (expr)
        statement
    else
        statement

或者它可以分组为:

if (expr)
    if (expr)
        statement
else
    statement

这两者之间的唯一区别是它们的缩进方式,C 忽略了这一点。在这种情况下,歧义通过使用第一种解释来解决,即 else 语句绑定到最近的前一个 if 语句。实现第二种解释,需要花括号:

if (expr) {
    if (expr)
        statement
}
else
    statement

但是,即使在第一种情况下,包含花括号也是个好主意,即使它们不是必需的:

if (expr) {
    if (expr)
        statement
    else
        statement
}

一般来说,在单条指令的语句或循环中,花括号是可选的;相反,如果您有两条或更多条指令,则必须添加它们。 例如:

for(i = 0; i < 2; i++)
   for(j = 0; j < 4; j++)
      If(...)
         printf(..);
      else
         printf(..);

相当于:

for(i = 0; i < 2; i++)
   {
         for(j = 0; j < 4; j++)
         {
             If(...)
             {
                 printf(..);
              }
              else
             {
                 printf(..);
             }
        }
   }

正如您可能注意到的,这与代码缩进有关。就我个人而言,如果我只有一条指令,我不会使用大括号,因为这样做会使您的代码更短更清晰。

使用大括号的另一个原因是,一个简单的错别字可能会让您大吃一惊:

#include <stdio.h>
int main(void) {
  if (0 == 1)
    printf("zero is one\n"),
  printf("is this inside the if statement?? /who kn0WS\n");
  return 0;
}

仔细看...