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

Jar*_*rod 49 c static function

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

unw*_*ind 62

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

static int addTwo(int x)
{
  return x + 2;
}
Run Code Online (Sandbox Code Playgroud)

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

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

更新:在许多情况下,做上述事情实际上是一个好主意,我意识到我的回答听起来非常黑白,这有点过于简单化了.例如,模拟(或仅使用)内部函数的代码可以像上面那样表达,并且使用显式inline关键字:

static inline int addTwo(int *x)
{
  __add_two_superquickly(x);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 好吧,编译器可能会内联短函数.因此,如果函数足够短,它实际上可以使用更少的内存.但我会添加一个"内联",因此您不会收到有关未使用的静态函数的编译警告. (12认同)

sha*_*oth 15

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


RBe*_*eig 7

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

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

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

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

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

  • 也许该函数包含在调用之间保留其值(内部状态)的静态变量,因此每个模块通过拥有自己的函数克隆来获得变量的“自己的私有副本”? (2认同)
  • @ranReloaded,这是有可能的。我个人会避免它,因为它会给聪明的优化器(或代码维护者)提供太多机会通过“聪明地”消除明显冗余的函数体来破坏它。此外,它为每个翻译单元提供了一组内部状态。将所有状态放入传递给每个调用的显式状态变量中会减少混乱,也不会那么脆弱。 (2认同)

gal*_*tte 7

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

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

但是这样做可能会有很大的不同,这在任何答案中都没有提到。如果您的函数使用静态局部变量,例如:

static int counter()
{
    static int ctr = 0;
    return ctr++;
}
Run Code Online (Sandbox Code Playgroud)

而不是:

//header
int counter();

//source
int counter()
{
    static int ctr = 0;
    return ctr++;
}
Run Code Online (Sandbox Code Playgroud)

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

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