在C++中,函数声明后面需要加分号(';')吗?

175

最近我参加了一场中级编程测试,其中有一个问题我做错了:

在函数声明后不需要分号 (';')。

是真的还是假的。

我选择了“假”(如果我错了,请纠正我,因为我感觉自己要疯了),函数声明是你在定义之前写的东西(在代码顶部),所以当调用它之前编译器就知道函数的名字,而函数定义则构成了整个函数。

例如:

声明:

int func();

定义:

int func() {
  return 1;
}

这个答案不应该是错误的吗?


42
“定义”也是一种声明。但我认为你的回答是正确的。 - user2100815
218
这是一个棘手的吹毛求疵的问题,与任何人编程能力无关。 - phonetagger
41
我总是觉得双重否定的问题很令人困惑。在我看来,这样的问题旨在使学生出错。这个问题为什么不能改成以下这种形式:“在函数声明后总是需要分号(';')吗?是或否?”?:/ - Algirdas Preidžius
19
这种混淆只能说明问题的陈述非常糟糕。 - François Andrieux
34
汉隆剃刀指出,测试作者混淆了“声明”和“定义”的概念。 - Sneftel
显示剩余18条评论
12个回答

163

你可以在一个步骤中声明并定义函数,也就是说,如果你在声明函数的地方包含函数定义。所以从技术上讲,我想 "true" 是正确的。但问题的措辞让我回答了你的方式。


10
我认为“true”这个词不太确切,因为你所给的理由。如果在某些情况下需要使用分号,那么“true”就是错误的(或者不准确)。对我来说,“true”是绝对的,如果有明确需要使用分号的情况,那么你就不能说它是“true”的。 - I Funball
16
@IFunball 说得好。自然语言有时候就是这么麻烦。句子“A semicolon (';') is not needed after a function declaration.”可以理解为“在函数声明后,分号(';')不需要 (永远不需要)”,也可以理解为“在函数声明后,分号(';')不需要 (并非总是需要)”。选择如何解释这个句子,决定了把它归类为真或假。严格来说,这个问题不够明确,因此没有明确的答案。 - Peter - Reinstate Monica
6
这是因为“declaration”通常被理解为“非定义性声明”,没有更多的背景信息和说明我们在进行语言律师工作。这个问题是不公平的。 - Lightness Races in Orbit
2
任何考试问题,如果对了解被测试内容的人来说不清楚,那么它就是有缺陷的。 - Nat
2
似乎我们需要在英语语言中添加一个未定义行为条款。 - Nick Mertin

148

除了 "定义也是声明" 这一点之外,以下内容在合法的 C++ 中也是成立的:

int f(), g();

这里声明了两个函数fg,它们都没有参数并且返回类型为int,但是f的定义后面没有紧跟一个分号。同样地,这也是合法的:

int f(), i = 42;

但在这些情况下完全省略分号是不允许的,因此如果将它们作为没有跟随分号的声明的例子,那么这将是令人惊讶的。实际上,以下写法是不合法的:

void *p, f() {}
除了函数声明外,函数定义不能与任何其他相同的类型说明符声明或定义结合在一起。(如果这是合法的,它将同时定义void *p和void f() {}。)总之,这似乎是一个“坑人的”问题,不应该出现在中级编程测试中。(顺便说一句,请不要真的写像int f(), i = 42;这样的代码。)

2
也可以使用typedef来定义一个函数类型,然后利用它一次性声明多个函数,例如:typedef int fooProc(int); fooProc a,b.c.d.e;我不确定为什么早期基于软盘驱动器的编译器标准头文件没有这样做,因为我认为它会使头文件更小,从而处理速度更快。 - supercat
还要考虑 int f(int(&g)(),int(*h)()){return g()+h();} 这个代码。它有三个函数声明,其中一个后面跟着一个左花括号,另一个后面跟着一个逗号,第三个后面跟着一个右括号。 - David Hammen
1
@DavidHammen:这并没有严格声明除了int f(stuff)之外的函数。即使在函数范围内,g是一个类型为函数引用的自动变量,而h是一个函数指针 - Peter Cordes

85
其他答案和评论指出了许多这个问题可怕、误导性和写得很差的方式。但是还有一个问题没有被其他人识别出来。问题是:
分号(';')在函数声明后是不必要的。是真的吗?
好的,让我们看一下函数声明:
int func();       /* */
/*           ^       */
/*           |       */
/* That whitespace is "after the function declaration". */

整个声明都在那里了。 声明不是int func(),然后后面跟着;。声明是int func();,然后跟着空格。

所以问题是:在声明后需要分号吗?当然不需要。 声明中已经有一个分号终止了它。在声明之后加上分号是毫无意义的。相比之下,int func(); ;将是函数声明后的分号

问题几乎肯定是要问“函数声明中最后一个标记是否总是分号”,但这不是他们写下的问题,因为测验的作者没有清晰地思考这个问题。

我的建议是完全避免编程语言测验。他们非常糟糕。


有趣的是,在C#中,这些都是合法的:

class C {}
class D {};
struct E {}
struct F {};
在C#中,类或结构声明可以选择以分号结束或不以分号结束。这个有点奇怪的特性是为了方便那些来到C#的C/C++程序员而添加的,他们已经习惯了类型声明以毫无意义的分号结束;设计团队并不想因为这个习惯而惩罚他们。 :-)

评论不是用来进行长时间的讨论的;此对话已经被移至聊天室 - Samuel Liew

25
你也可以像这样声明一个函数:
int func(){
    return 1;
}

这个陈述非常模糊。正确的答案应该是:取决于你如何声明函数。

无论如何,我也会选择false,并且也许你可以向某人报告此问题。


3
无论如何,不要把这件事情放在个人层面上。重要的是你理解了函数声明-定义的工作原理,所以不要太担心了,只要确保问题至少被检查过,并继续进行即可。 - Luca Corsini
11
当然。说实话,我从答错这个问题中学到的有关函数声明定义的知识比如果我回答正确所学到的还要多。 - Logan
1
@Logan不要太担心。如果你知道如何编写和阅读函数,那就足够了。我个人讨厌这种问题,因为1. 它们没有很好地定义2. 测试你对语法的理论知识。对我来说,这就像肌肉记忆一样。当我写每个数字时,它们会轻松地到达它们应该去的键上,但是如果你让我做一个关于哪些键应该按下数字的测试,如果没有键盘来实际执行操作,我将完全无助... - bolov
2
编写常用语法(例如函数)将成为您的第二天性。当您因为换了语言而犯错时,智能感知和语法突出显示可以提供快速高效的解决方案。把时间和精力投入到更有用的事情上。 - bolov

20

函数声明后不需要使用分号(';')。

是或否?

是的。任何声明、定义和语句后都不需要使用分号。

许多种声明需要以分号结束,详见第7节[dcl.dcl]中的语法规定。但在此之后再写一个分号是没有必要的。


1
我看到Eric Lippert已经辩论过这个观点了。我想所有的赞让我忽略了它。请随意在那里投票。 - Marc van Leeuwen
几乎任何问到“X总是正确的:是或否?”的问题都会有答案“否”。实际上,根本没有必要在任何地方使用分号;编译器可能会抱怨并拒绝编译您的程序,但这并不是世界末日;我不会称其为基本需求。;) - Quuxplusone
如果编译器拒绝您的程序,那么您的程序中没有任何函数声明 :) - Ben Millwood

6
这取决于我们是在声明还是定义函数。 如果我们在声明函数,需要包含分号(;),如果在定义函数,则不需要分号。
声明如下:
int add(int, int);

以下是定义:

int add(int a, int b)
{
    // ...
}

10
这个答案存在问题,因为它暗示定义和声明是互相排斥的。实际上,每一个定义都是一个声明;定义是声明的一部分。 - MSalters

6
很遗憾,你提出的问题没有明确说明“紧接着之后”。例如,我们可以这样写:
int func()  /* My function */ ;

或者我可以写:

int func()
int a = 42;

在第一种情况下,分号不是直接跟在声明后面的,但那也没问题。
在第二种情况下,有一个分号“在”声明后面,但不是直接跟在后面。
我认为Eric Lippert在他的回答中有正确的想法。
这就像说“在英语句子结束后应该有句号吗?”。可以说,句子已经在结尾处有了句号(否则它就不是句子),因此在句子后面不应该有句号。

4
好的。在那个句子结尾加了一个额外的句点。我明白你的意思了。 - David S
2
int func() int a=42;无法编译。您需要逗号,而不是另一个“int”。请参阅@Arne在此之前发布的答案。此答案中唯一新增的内容是最后一段,类比于英语句子。 - Peter Cordes
1
我并没有说第二个例子编译通过。我只是指出说在声明后需要“加上”分号是含糊不清的。我的例子在声明后有一个分号,但它无法编译通过。 - Nick Gammon
1
这个问题也会出现在错误信息中;C# 中一个常见的例子是“params 参数必须是形式参数列表中的最后一个参数”。现在,假设我说“在 gloob 列表中,frob 必须是最后一个 gloob”。这是否意味着(1)每个 gloob 列表在结尾都有一个 frob,就像每个问题在结尾都有一个问号一样,(2)gloob 列表可以有任意数量的 frobs,但如果它有一个或多个 frobs,则最后一个项目必须是 frob,就像偶数可以有任意数量的 02468,但其中一个必须是最后一个,或者... - Eric Lippert
(3)一个gloob列表可以有零个或一个frobs,如果有一个,它在最后出现?如果您不知道上下文,我认为(1)是最合理的解释,但在“params参数”的情况下,(3)是正确的解释。许多编程语言元素的非正式描述具有我的技术编辑朋友称之为“COIK”的属性--只有已知才清晰明了。如果您还没有彻底理解材料,对其进行描述对您毫无用处,但如果您已经彻底理解它,您就不需要描述! - Eric Lippert

6
尽管我几乎同意其他答案,认为问题表述非常模糊,你的答案在技术上是正确的,但请允许我提供另一种观点:
这就是我一直称呼它们的方式:
void func();  // The function prototype

...

void func()
{
    // The function definition
}

我假设问题是以这个术语为背景的。

在我看来,“定义”和“声明”是相同的概念。"我定义 x = y" == "我声明 x = y"。

但当然,在函数原型(顶部)和函数的实际定义之间有很大的区别。


对我来说,你的原型是基于我所学的声明(并不是说你错了),但我也期望原型指定参数的数量和类型,或者是 void,但我预计你为简洁起见省略了这些信息。 - David S
David S:当然,它也会包含参数的数量和类型,但出于简洁起见,我确实省略了它们(请注意,实际函数声明中也没有参数)。然而,当你说完整的函数声明被称为原型时,我并不完全同意。我引用维基百科的话:“函数原型或函数接口是一个函数声明,它指定了函数的名称和类型签名(参数的数量、数据类型和返回类型),但省略了函数体。” - Opifex
@DavidS:在C++中,函数声明总是原型(或定义),void func();void func(void);完全等价。这与C非常不同,其中void func();对编译器不提供有关参数的任何信息,并且与void func(void);不同。稍后的原型或定义是一个好主意,否则调用者必须应用默认参数提升(例如float->double和窄整数类型到int。与可变参数函数的参数相同规则)。 - Peter Cordes
非常抱歉,我最初是在查看与C语言相关的内容时来到这里的,并没有注意到语言的变化。为了保持清晰度,我不会删除我的评论,但请将其撤回。 - David S

4
您只能在原型中使用分号;

4

这是一个有些棘手的问题,但他们使用了单词declaration,其意思类似于:

int example();

所以在这种情况下是正确的。

如果他们使用了“实现”这个词,则会是错误的。


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