为什么在C代码块中要使用花括号?

68

我在查看一些C代码时,发现它充满了这些花括号,围绕着没有任何控制结构的代码块。看一下:

//do some stuff . . .
fprintf(stderr, "%.2f sec\n", (float)(clock() - t) / CLOCKS_PER_SEC);
{
    //a block! why not?
    char *tmp_argv[3];
    tmp_argv[0] = argv[0]; tmp_argv[1] = str; tmp_argv[2] = prefix;
    t = clock();
    fprintf(stderr, "[bwa_index] Convert nucleotide PAC to color PAC... ");
    bwa_pac2cspac(3, tmp_argv);
    fprintf(stderr, "%.2f sec\n", (float)(clock() - t) / CLOCKS_PER_SEC);
}

为什么要在代码中插入这样的块?它们到处都是。有什么性能优势吗?某种神秘的C语言东西吗?为什么?编辑:此代码来自BWA,一款生物信息学程序,使用Burrows-Wheeler transform将小序列与大参考序列进行比对。如果您想知道,这个代码示例与应用程序的功能并不特别相关。

1
该实践在C和C++中可能有不同的动机。您的代码看起来像是C,而您的问题被标记为“C ++”。您确定已正确标记问题吗? - AnT stands with Russia
1
啊,好发现。这确实是C语言。我不知道为什么我以为它是C ++。已更改标签。 - Jamison Dance
然后编辑文本。它仍然说“我正在查看一些C++代码...” - AnT stands with Russia
9个回答

97

旧代码需要{ }才能进行声明

在C89中,你不能随处写 int i;,声明只在块的开头有效。

因此:

a = 1;
int i; /* error */
i = 2;

...不合法,但是

a = 1
if (e) {
  int i;

当时使用普通块也是可以的。

即使在声明变为有效(C99)的块项后,这种结果的样式仍然保持不变,部分原因是惯性,部分原因是为了向后兼容性,还有一部分原因是建立新声明的作用域是有意义的。


2
严格来说,在 C 语言中,声明从未成为“语句”。即使在 C99 中,声明也不是语句。在 C++ 中它们是语句,但在 C 中不是。 - AnT stands with Russia
12
基于这个答案,它可能表明纯块语句已不再必要。实际上,很多人喜欢继续使用它来限制他们创建的变量的作用域,即使在块内部声明新变量时已不再需要这样做。我认为作用域的答案比传统的回答更重要,尽管C89兼容性也是这样做的一个好理由(因为很少有编译器完全实现了C99)。 - Chris Lutz
@AndreyT:唉,你是对的,当时以那种方式说出来确实让我有点不舒服。仅供记录 :-) C89:compound-statement ::= { declaration-list(opt) statement-list(opt) },而在C99中:block-item ::= declaration | statement。 - DigitalRoss
如果你回到足够早的时候,在你的家庭复古计算机实验室或虚拟机中,你只能得到函数块的开头,而没有内部块。 - DigitalRoss

42

对变量进行作用域限制。例如,变量tmp_argv只在大括号之间有效。


9
虽然在这里看起来并非如此,但作用域的结束会触发析构函数,否则这些析构函数将在稍后才被触发。 - Keith Randall
2
@Keith:+1 我以前在我的多线程代码中经常这样做,创建一个类,在它的构造函数中锁定互斥量,在析构函数中解锁。 - Jherico

14

我最近发现这个功能的另一个用途是当你有开/关语义并且想要清楚地标记“内部”代码时:

f = fopen('file');
{
    // do stuff
}
fclose(f);

这很好地提醒你关闭/释放对象,并使代码有些更加整洁。


7

一个块是一个作用域,它决定了变量的生命周期以及它们对编译器的可见性。因此,在块内创建的变量会在控制流程退出块时消失。

当这些变量是具有构造函数和析构函数的类的实例时,它非常方便。

然而,在您的示例中并没有太多优势。


6

这段代码是创建作用域。当堆栈对象超出作用域时会被销毁。看起来它在进行某种类型的计时,这意味着每个块都是需要计时的内容。然而,我没有看到任何作用域计时器对象,所以这段代码没有意义。


6

你在代码块内声明的变量是局部变量。这样,你就可以在代码的其他地方(下面)重新定义tmp_argv,而不会与此段代码发生冲突。


1

就这些吗?也许程序员在代码的其他地方使用了tmp_argv。我想不出其他原因,因为大括号内部的tmp_argv与大括号外部的任何内容都是分开的。


不,还有很多。我只是随便拿了一个例子。 - Jamison Dance
那么程序员在花括号外部的作用域中使用 tmp_argv 吗? - Jacob

1

我有时会在以下情况下使用代码块: - 用于本地化变量 - 或者为了更易于阅读 ...


-5
嗯——也许我有点离谱,但我认为在这样的块内定义的局部变量将在块外无效。

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