【问题标题】:In C, should I define (not declare/prototype) a function that takes no arguments with void or with an empty list?在 C 中,我应该定义(不是声明/原型)一个不带 void 或空列表参数的函数吗?
【发布时间】:2016-08-18 11:57:04
【问题描述】:

这个问题可能有也可能没有重复,虽然我试图找到一个,但每个人的答案似乎只是指声明/原型。他们指定void foo() { } 的定义与void foo(void) { } 相同,但我应该实际使用哪种方式?在 C89 中?在 C99 中?我相信我应该开始将void foo(void); 用于我的prototype 声明,但是如果我使用void 或不用于定义,有什么区别吗?

【问题讨论】:

  • 两者都可以,我听到的关于void foo (void) {} 的唯一论点是它明确表示不需要任何参数——有助于提高可读性。
  • 混淆的根源在于void关键字在C89之前并不存在。因此,一些坏习惯一直延续到今天。没有参数的函数的正确定义是void foo(void)
  • 声明和定义必须一致
  • 它们必须匹配verbose。
  • @AlterMann 非常感谢。

标签: c


【解决方案1】:

我会尽量简单实用地回答。
从我熟悉的实践和参考来看,c89 和 c99 应该平等对待函数的声明/定义/调用不带参数且不返回值。 如果省略原型声明(通常在头文件中),定义必须指定所采用的参数的数量和类型(即它必须采用原型的形式 , 明确地 void foo(void) 接受 no 参数) 并且应该源文件中的实际函数调用之前(如果在同一个程序中使用)。作为良好编程实践的一部分,我一直被建议编写原型和适当分段的代码。

声明:

void foo (void); /*not void foo(), in order to conform the prototype definition !*/

定义:

void foo (void) /*must match its prototype from the declaration !*/
{
  /*code for what this function actually does*/
  return;
}

从 main() 或其他函数中调用函数:

...
  foo();
...

【讨论】:

  • 这正是我正在寻找的答案。谢谢你。在阅读了其他所有内容和所有类似问题后,我有点想通了,但你是第一个意识到我不是在谈论非定义声明,而是明确询问我应该使用哪个来进行定义声明。
  • 我将从现在开始在 C 中的原型和定义中使用T Foo(void)。我通常什至不考虑设置-std=c11 甚至c99 所以我想我通常使用c90 作为基础语言?即gnu90 我想??或者是gnu89 还是那些基本上是别名?为什么 gcc 不像 gnu99 或 gnu11 那样使用?还是c11或c99?我应该默认开始使用 c11 吗?我从来没有在 Windows 上运行我的代码。
  • 请务必查看针对您正在使用的目标平台和编译器建议的任何文档。对我来说,c99 在大多数情况下都有效。这是一个 SO 帖子的链接:link
  • 好吧,看来是的,我没记错,gnu89gnu90 均指 c90 + 扩展。
【解决方案2】:

是的,有区别。最好定义像 void foo(void){} 这样的函数,因为它会阻止在编译时将任何参数传递给函数,错误如下:too many arguments to function 'foo'

编辑:如果您想为现有代码添加此类编译器的验证,这可能可以通过更改标头中的原型来完成。无需更改函数定义。但它看起来很尴尬恕我直言。所以对于新创建的程序(正如上面熟练的评论员所指出的),最好使定义和声明匹配详细,这是用空括号声明和定义的糟糕且古老的做法

【讨论】:

  • 这也是一个很好的答案,它实际上提到了 definiton 声明要做什么。这是我接受答案的第二选择。
【解决方案3】:

它们在语义上是不同的

给定以下函数:

void f(void);
void g();

使用参数调用f 是编译时错误:

error: too many arguments to function "f"

但是,g 的声明意味着它需要 未指定 数量的参数。对于编译器来说,这意味着它可以接受任意数量的参数,从零到某个实现定义的上限。编译器将接受:

g();
g(argument);
g(argument1, argument2, ... , argumentN);

本质上,因为g 没有指定它的参数,编译器并不真正知道g 接受多少个参数。所以编译器会根据g的实际用法接受任何东西并发出代码。如果您传递一个参数,它将发出代码来推送一个参数,调用g,然后将其从堆栈中弹出。

这是明确说“不,我不接受任何论据”和在被问到时什么都不说之间的区别。保持沉默会使问题变得模棱两可,以至于调用g 的语句是编译器关于函数接受哪些参数的唯一具体信息。因此,它将根据 that 规范发出机器码。

建议

我应该实际使用哪种方式?

According to the SEI CERT C Coding Standard,建议在函数不接受参数时显式指定void

文章引用了 C11 标准的第 6.11.6 条作为其推荐的基础:

使用带空括号的函数声明符 (不是原型格式参数类型声明符) 是一个过时的功能。

使用未指定的参数列表声明函数被归类为中等严重性。给出了可能出现的问题的具体例子。即:

  1. 不明确的接口
    • 编译器不会执行检查
    • 可能会隐藏错误
  2. 信息流出
    • 潜在的安全漏洞

Information Security has a post 不仅探索安全性,还探索这两种风格的编程和软件开发含义。

问题更多是关于质量保证。 旧式声明是危险的,不是因为邪恶的程序员, 但是因为人类程序员,他们无法想到所有事情 并且必须得到编译器警告的帮助。这就是功能的全部意义所在 原型,在 ANSI C 中引入,其中包括类型信息 函数参数。

【讨论】:

  • 我专门询问作为定义一部分的声明。但是我在浏览完所有内容后得到了答案。无论如何,+1 以获得很好的解释(我已经知道使用 void 来表示 not 定义的一部分的声明有多空。
【解决方案4】:

它们是不同的,void foo(void)foo 声明为一个没有参数且不返回任何内容的函数。

而对于void foo(),函数foo 接受未指定数量的参数,并返回void。

对于符合标准的 C,您应该始终使用第一个。

【讨论】:

  • 我认为这仅适用于不属于定义的声明?我明确询问我应该使用哪个定义,因为对于定义,void foo() { }void foo(void) { } 都指定该函数采用 no 参数。即,声明是否是定义的一部分很重要。
  • @RastaJedi 声明应始终与定义匹配。正如答案所说,空参数列表并不意味着“没有参数”。
  • 定义的空列表应该意味着没有参数..?
  • @user694733 6.7.5.3 函数声明器(包括原型)说:14 函数声明器中的空列表是该函数定义的一部分,它指定该函数没有参数。
  • @RastaJedi 因为它们在声明中的含义不同,并且应该始终匹配声明和定义,因此您不应该使用void foo() {...}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多