C / C ++:头文件中的静态函数,是什么意思?

时间:2009-04-23 07:59:01

标签: c function static

我知道在源文件中声明静态函数时的含义。我正在阅读一些代码,发现头文件中的静态函数可以在其他文件中调用。

6 个答案:

答案 0 :(得分:61)

是否在头文件中定义了函数?因此实际代码直接在函数中给出,如下所示:

static int addTwo(int x)
{
  return x + 2;
}

然后,这只是为许多不同的C文件提供有用功能的一种方式。包含标头的每个C文件都将获得它可以调用的自己的定义。这当然浪费了内存,并且(在我看来)是一件非常难看的事情,因为在标题中包含可执行代码通常不是一个好主意。

请记住#include:标题基本上只是将标题的内容(以及它包含的任何其他标题)粘贴到编译器看到的C文件中。编译器永远不会知道一个特定的函数定义来自头文件。

更新:在很多情况下,做上面这样的事情实际上是一个好主意,我意识到我的回答听起来非常黑白,这有点过于简单化了。例如,建模(或仅使用)intrinsic functions的代码可以像上面那样表达,甚至可以使用明确的inline关键字:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}

这里,__add_two_superquickly()函数是一个虚构的内在函数,因为我们希望整个函数基本上编译成单个指令,所以我们真的希望它被内联。不过,上述内容比使用宏更清晰。

直接使用内在函数的优势当然是将它包装在另一个抽象层中,可以在缺少特定内在函数的编译器上构建代码,方法是提供备用实现并根据哪个编译器选择正确的实现正在使用。

答案 1 :(得分:13)

它将有效地在包含它的每个cpp文件中创建一个具有相同名称的单独静态函数。这同样适用于全局变量。

答案 2 :(得分:7)

正如其他人所说,它与static文件本身中的.c函数具有完全相同的含义。这是因为.c.h文件之间没有语义差异;只有编译单元由实际传递给编译器的文件组成(通常名为.c),其中包含#include行中命名的任何和所有文件的内容(通常命名为.h)在预处理器看到它们时插入到流中。

C源位于名为.c的文件中,而公共声明位于名为.h的文件中的约定只是一种约定。但它通常是一个很好的。根据该约定,.h文件中应该出现的唯一内容是声明,这样您通常可以避免在单个程序中多次使用相同的符号定义

在这种特殊情况下,static关键字使符号对模块是私有的,因此没有多重定义冲突等待引起麻烦。所以从某种意义上讲,这样做是安全的。但是,如果没有保证函数将被内联,那么您将承担在#include发生在.h头文件中的每个模块中实例化该函数的风险,该文件最多会浪费代码段中的内存

我不确定在一般可用的公共标题中用什么用例可以证明这一点。

如果.c文件是生成代码并且只包含在单个.h文件中,那么我个人会将文件命名为#include以外的文件,以强调它实际上不是一个公共标题。例如,将二进制文件转换为初始化变量定义的实用程序可能会编写一个旨在通过static使用的文件,并且很可能包含变量的static声明,甚至可能访问者或其他相关效用函数的{{1}}定义。

答案 3 :(得分:3)

如果在头文件中定义函数(不是简单地声明它),将在每个翻译单元中生成该函数的副本(基本上在包含此标题的每个cpp文件中)。

这可能会增加可执行文件的大小,但如果函数很小,这可能会微不足道。优点是大多数编译器可以内联函数,这可能会提高代码性能。

但是这样做可能存在重大差异,这在任何答案中都没有提及。如果您的函数使用静态局部变量,例如:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}

而不是:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}

然后包含此标头的每个源文件都有自己的计数器。如果函数在头文件中声明,并在源文件中定义,则计数器将在整个程序中共享。

所以说唯一的区别是性能和代码大小是错误的。

答案 4 :(得分:1)

在源文件或头文件中定义没有语义差异,在使用静态关键字时,基本上两者在普通C中都是一样的,你限制范围。

但是,在头文件中写这个有问题,这是因为每次在源文件中包含头时,你都会得到一个具有相同实现的函数副本,这与具有正常函数非常相似在头文件中定义。通过在标题中添加定义,您无法实现静态函数的用途。

因此,我建议您只在源文件中实现,而不是在头文件中。

答案 5 :(得分:1)

在一些具有小内联函数的“仅标题”库中很有用。在这种情况下,您总是希望复制该函数,因此这不是一个糟糕的模式。但是,这使您可以轻松地在单个头文件中插入单独的接口和实现部分:

// header.h

// interface part (for user?!)
static inline float av(float a, float b);

// implementation part (for developer)
static inline float av(float a, float b)
{
    return (a+b)/2.f;
}

GLK框架中的Apple矢量数学库使用这样的构造(例如GLKMatrix4.h)。