C/C++需要本地函数原型吗?

4

在C/C++中,显式原型化局部函数是否比在使用前定义函数具有优势? 这里的局部函数指仅在其源文件中使用的函数。 例如:

#include "header.h"

static float times2(float x){
    return 2*x;
}

static float times6(float x){
    return times2(3*x);
}

int main(void){

    // Other stuff ...

    float y = times6(1);

    // Other stuff ...
}

与之相比:

#include "header.h"

// Local function prototypes
static float times2(float);
static float times6(float);

// Main
int main(void){

    // Other stuff ...

    float y = times6(1);

    // Other stuff ...
}

// Local functions definition
static float times2(float x){
    return 2*x;
}

static float times6(float x){
    return times2(3*x);
}

个人而言,我更喜欢使用第一种选项,因为需要编写的代码较少,文件也更易读。但现在我想知道是否有任何技术原因偏好第二种选项。

编辑:我已将static添加到times2()和times6()中,请参见@Gangadhar的答案和评论。


7
唯一的技术原因是它们相互引用。除此之外,这完全取决于风格。 - nikolas
你应该将“locals”放入匿名命名空间中,这样它们不会获得外部链接。 - John3136
@nijansen:你能把你的评论发表为答案吗?(你是第一个指出交叉引用情况并清楚地说明否则没有技术上使用选项1的理由的人。对我来说,这就是问题的答案。) - Milo
@Milo,我已经添加了一个答案,但我仍然想鼓励你选择你认为最有帮助的答案。 - nikolas
5个回答

3
除了在需要进行前向引用时,声明本地(static)函数的优点之外,其优点还在于组织性。静态变量也适用于同样的理由。
如果一个文件有几十个函数(和变量),先前的局部定义可以帮助组织。你可能希望将这样大型集合按顺序(A-Z)保留下来。但是这个顺序可能会对前向引用产生影响。通过列出所有函数/变量(全局的在(*.h)文件中,本地的在此处(*.c)),即使代码的生命周期中的前向引用需求发生变化,组织仍然可以保持。
// function/variable prototypes
static float pi;
static float pi2;
static float times2(float);
static float times3(float);
static float times4(float);
static float times5(float);
static float times6(float);
static float times7(float);
static float times8(float);
static float times9(float);

2

有时候你需要预先声明函数原型,也就是说,在使用函数之前编译器需要知道函数的原型。

考虑以下这些没有特别用处的函数:

int foo(int x)
{
    if(x < 1) return x;
    else return x + bar(x-1);
}

int bar(int x)
{
    if(x < 3) return x;
    return x * foo(x-1);
}

如果你尝试编译这段代码,编译器会报错:

错误:'bar'在此作用域中未声明

你需要在调用该函数的函数前添加缺失的原型声明:

int bar(int);
// as above unchanged

这是唯一一种情况,编译器要求您在函数之前放置函数原型。在所有其他情况下,随时放置原型都是完全合法的,因此以下写法也是合法的:

int foo(int);
int bar(int);
int foo(int);
int bar(int);

尽管这显然是多余的(请不要这样做)。一些人认为,在文件顶部放置每个函数的函数原型是一种良好的风格,因为:
  1. 您不必再关心编译器要求您这样做的情况,而有些人显然无法解释编译器的错误消息(我认为这非常简洁),以及
  2. 您可以在一个视图中看到文件提供的函数以及它们如何被调用。
但这只是一个风格上的讨论。我喜欢尽可能地保持我的代码简短,所以我只会使用编译器需要的原型,但这纯粹是品味和约定的问题。入乡随俗或类似的东西。

谢谢你的回答。我的问题并不是关于风格的,我只是想确认使用选项1(这也是我自己喜欢的风格)是否会有任何技术上的副作用。 - Milo

0
如果提到原型:函数times2、times6在调用时期望浮点参数在堆栈或寄存器中。如果省略原型,编译器将无法强制执行此操作,上述函数最终会在堆栈上操作其他数据。通过包含函数原型,您告知编译器两个函数都需要一个浮点参数,并使编译器能够捕获此类错误。

虽然从理论上讲是一个好的观点,但在实践中,我从未见过C++编译器允许在调用站点之前未在翻译单元中声明的函数的使用。我猜标准不允许这样做,尽管我可能错了。 - Kevin
1
这个答案给出了一个函数声明在使用之前应该在作用域内的原因。但这不是问题所在。问题是关于在使用之前只有声明还是在使用之前有定义(也是一个声明)。在两种情况下,声明在使用之前都在作用域内。 - Eric Postpischil

0
当两个函数相互调用时,即FuncA()调用FuncB(),反之亦然,则必须声明其中任何一个函数。这是@MM(上面的答案)所描述的条件。
但在任何正常情况下,声明函数只是一种惯例,因为它因开发人员而异。

0

只有需要前向引用和 extern 才是使用原型的唯一原因。原型是噪音。在使用函数之前声明它。这样更清晰,并且只需要您更改一次接口。我不明白为什么人们要使用它们。


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