在C语言中,什么时候可以省略花括号?

25

在C语言中,我什么时候可以省略花括号?我之前见过没有花括号的return语句,例如:

When can I omit curly braces in C? I've seen brace-less return statements before, such as


if (condition)
  return 5;

但是这种方法似乎并不总是适用于所有语句,例如声明方法时。

省略大括号的规则与Java中的规则相同吗?


5
好的,每当语言期望一个“语句或块”时,它都能正常工作... - Kerrek SB
3
你的问题中的代码是没问题的。试着展示一些你不明白的东西。 - David Heffernan
7
即使在那个特定的时间点上它们不是必需的,使用这些大括号是一个非常好的想法。人们会添加代码,并且有了它们,就不太可能将额外的代码放在正确的位置。按下几个额外的按钮有什么损害 - 比周五下午试图弄清楚代码为什么无法按预期工作要容易得多! - Ed Heal
3
@EdHeal 我完全同意。我想不出任何一种情况,这种情况中省略花括号会更好。 - Dan F
2
通过“声明一个方法”,我认为你的意思是“定义一个函数”。 - Keith Thompson
显示剩余2条评论
10个回答

26

只有当条件语句 (if-else)、循环语句 (forwhiledo-while) 或者 switch 语句的执行体只包含一个语句时,才可以省略大括号:

if (cond)
  do_something();

for (;;)
  do_something();

while(condition)
  do_something();

do 
  do_something();
while(condition);

然而,请注意以上每个例子在语法上都被视为单个语句;这意味着你可以编写类似以下的内容:

if (cond1)
  if (cond2)
    do_something();

这是完全合法的;if (cond2) do_something(); 会被简化为一个语句。同样地,if (cond1) if (cond2) do_something(); 也是一个语句,因此您可以使用类似 if (cond1) if (cond2) if (cond3) do_something(); 的方式进一步陷入疯狂。

for (i=0; i < N; i++)
  if (cond1)
    if (cond2)
      while (cond3)
        for (j=0; j < M; j++)
          do_something(); 

不要那样做。


3
有时候在switch语句中可以省略花括号。 - SJHowe
有时候,@SJHowe。 - user129393192

15
如果你看一下C语法,会发现有很多情况需要一个“语句”,这个术语由语法本身定义。
在任何这些情况下,你可以使用的语句形式之一是一个“复合语句”,它由一个开括号“{”,一个零个或多个声明和/或语句的序列以及一个闭括号“}”组成。(在C89中,所有在复合语句中的声明必须在所有语句之前;C99取消了这个限制。)
一个“函数定义”特别需要一个“复合语句”,而不仅仅是任何一种语句。(我相当确定这是唯一一种只能使用复合语句的语句类型)。如果没有这个限制,你将能够编写:
void say_hello(void) printf("Hello, World!\n");

但由于大多数函数定义包含多个声明和/或语句,允许这样做并没有太多优势。

还有一个单独的问题:什么时候应该省略大括号。在我个人看来,答案是“很少”。这个:

if (condition)
     statement;

是完全合法的,但这个:

if (condition) {
    statement;
}

在我看来,使用IMHO更加流畅且易于维护(如果我想要添加第二个语句,花括号已经在那里了)。这是我从Perl中学到的习惯,它要求在所有这种情况下都使用花括号。

唯一不使用花括号的时候是当整个 if 语句或类似的东西适合单行,并且这样做可以使代码更易读,并且我不太可能想要添加更多语句到每个 if 中时:

if (cond1) puts("cond1");
if (cond2) puts("cond2");
if (cond3) puts("cond3");
/* ... */

我发现这样的情况相当罕见。即使在这种情况下,我仍然会考虑添加大括号:

if (cond1) { puts("cond1"); }
if (cond2) { puts("cond2"); }
if (cond3) { puts("cond3"); }
/* ... */

4
作为附注,一些编码标准要求在所有迭代语句(如iffor等)中始终使用{},即使只有一个语句。MISRA-C是一个例子。 - ouah
有道理!已经有大括号意味着(a) 你以后不必记得它们,(b) 如果你忘了记住它们,你不会冒险造成灾难性的结果(这可能是MISRA的主要关注点),(c) 如果你的编码风格说不使用括号时要将它们放在同一行作为初始语句,并且你在单/多行之间转换(并记得添加/删除括号...),你的版本控制系统不会充斥着差异噪音。 - underscore_d

3
一个例子:
int a[2][2] = {{1, 2}, {3, 4}};

您可以使用有效的等价形式:

int a[2][2] = {1, 2, 3, 4};

一些冗长的编译器可能会发出警告,但这是有效的。
请注意,在if语句(同样适用于while语句,for语句等)中,{}不会被省略。它们只是没有被要求。if语句的语法如下:
if (表达式) 语句
如果你想要多个语句而不是单个语句,可以使用一个被{}包围的复合语句。

3

当你想要将多个语句或表达式组合成一个时,主要需要使用花括号,例如:

{
  x = 1;
  y = 2;
}

如果您将以上内容放置在ifelse中,则花括号中的整个内容将作为一个整体执行,而如果省略花括号,则只有第一个内容(在此示例中为x = 1;)将被用作ifelse的一部分。
通常您还会将它们与switch()一起使用。
switch (x)
{
case 1:
  // Do something
  break;
case 2:
  // Do something else
  break;
}

通常在使用do-while语句时需要它们:

do
{
  printf("%d\n", x);
  x++;
} while (x < 10);

您需要在代码中间定义和使用临时变量时,使用C89编译器来进行操作:

C89编译器使这种操作成为可能。

int main(void)
{
  puts("Hello, World!");
  {
    int x;
    for (x = 0; x < 10; x++)
      printf("%d\n", x);
  }
  return 0;
}

您可以使用它们来开始和结束函数体、结构/联合定义、枚举定义、结构/联合/数组的初始化,例如:

void blah(void)
{
}

enum e
{
  e1 = 1,
  e2 = 2
};

struct s
{
  int foo;
  int bar;
} s = { 1, 2 };

int a[3] = { 1, 2, 3 };

您有时可以在初始化中省略它们,例如:
char str2[] = { "abc" };
char str1[] = "abc";

是的,这也是有效的。但是对于字符串字面量示例,不是花括号可以被省略,而是在特定情况下,初始化程序可以选择用 {} 包围。 - ouah

3
但是这种方法并不总是对所有语句都有效。具体来说,当只需要一条语句时,它就是完全有效的。循环、if语句等都期望一条语句,要么是一个块,要么就是一个没有被包含在块中的单个语句。

2
当您执行单个语句时,可以省略它们:
for(...)
  printf("brackets can be omitted here");

if(...)
  printf("brackets can be omitted here");
else
  printf("brackets can be omitted here too");

等等...您总是可以添加它们。这样做从来没有坏处,而且有助于澄清范围,但您非常安全。我能想到的唯一“陷阱”是如果您尝试在该范围内进行声明(顺便说一句,这是无用的):

if(...)
  int a = 5;

这会导致错误,但这是因为您可以使用花括号执行单个语句,而不是声明。这是一个重要的区别。
严格来讲,如果使用逗号运算符连接多个操作,甚至可以在没有花括号的情况下执行多个操作...虽然我不建议这样做,但这值得注意:
if(...)
    value++, printf("updated value with brackets");

关于你的第二个问题,你可以在Java规范中看到规则是相同的。我特别链接到了if部分,但你可以看到在if后面需要一个语句,因此你可以跳过花括号。


2

每当有一个代码块有多行(将每个语句的分号算作一行)时

例如:

for (i = 0; i < j; i++ ) 
   foo(i);

while (1) 
   foo();

if( bool )
   foo();

void cheese()
   printf("CHEESE");

上述行中分号后面的所有内容都不被视为块内部,就好像有 { } 一样。


在函数声明的情况下,它不起作用! 它需要大括号。当我尝试时,我得到了以下内容- braces.c:在函数“foo”中: braces.c:4:错误:在“printf”之前需要声明说明符 braces.c:7:错误:在“{”标记之前需要“=”,“,”,“;”,“asm”或“__attribute__” braces.c:9:错误:期望输入结束处的“{” - Darshan L
1
是的,声称在函数定义中可以省略大括号的示例是错误的。 - underscore_d

2
如果不确定,使用大括号。它们不会“花费”任何额外的东西(除非您完全使用它们不正确)。
一个相当基本的规则是,“如果在同一缩进处有多个分号,则应该使用大括号”。这并不完全简单,但作为“一个句子,没有任何条件或其他复杂性”,它将不得不做。
是的,Java基于C语法具有相同的基本规则 - 可能存在一些Java与C或C ++不同的奇怪之处,但那是不寻常/奇怪的事情,不是您编写典型代码时通常遇到的问题(我没有做过很多Java,所以我不能说我发现了其中任何一个,但我非常确定有一些差异 - 毕竟它们是不同的语言)。

0

以下似乎有点棘手:

if (...)

        printf("I'm conditional\n");

我猜C预处理器会处理空行,所以这个语句是没问题的。当然这是非常不好的实践方法。


C++中的多余空格根本不重要;预处理器很可能会删除除了关键字、标识符等之间的最小要求以外的任何内容,而且无论如何编译器都不会注意到它。在if后面的下一条语句或块将被用作其主体,即使它们相隔20个空行并且没有正确缩进。误导性缩进是另一个问题,但编译器终于开始警告这个问题了。基本上,将大括号放在所有东西周围,并停止思考!绝对不要把C当成Python... - underscore_d
请问您能解释一下您的答案吗?根据帮助中心的说法:“......始终要解释为什么您提供的解决方案是合适的以及它是如何工作的”。请通过编辑(更改)您的答案来回应,而不是在评论区回复(不要添加“编辑:”、“更新:”或类似的内容——答案应该看起来像是今天写的)。 - Peter Mortensen

0
显然,人们可以在不使用花括号的情况下使用for循环,并且不必实际离开括号!
#include <stdio.h>

int main(){
    for(int i=0; i<10;i++, printf("%d ", i), printf("%d\n", i));
    return 0;
}

1 ... 10

1 ... 10

我从另一个帖子中复制了自己的答案。


另一个问题可能是*没有花括号的'for'循环会做什么?*。 - Peter Mortensen

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