我遇到了一些在头文件中有一个大的静态函数的代码,我只是好奇它是不是可以做到这一点。例如,如果许多.c
个文件包含标题,为什么不定义非静态函数并将其链接到?
关于何时/何时不将静态函数定义放在C中的头文件中的任何建议或经验法则,我们将不胜感激,
谢谢
答案 0 :(得分:25)
一些想法:
mylib123__foobar
和#define foobar mylib123__foobar
,所以这个看起来有点不合适。)也许该函数只是外部函数的包装器,包装器的工作方式可能取决于调用编译单元中的编译时选项。例如:
static int foobar(int x)
{
return real_foobar(COMPILETIME_PARAMETER, x);
}
你可能会说只使用宏,但是如果需要通过函数指针调用foobar
来实现预期的用途呢?
有人说过......
实际上,人们将static
函数放在头文件中的主要原因通常是基于一些10年过时的概念,它会通过允许编译器内联函数或诸如此类来提高性能。大多数这样做的人都没有做过任何测量。由于现代编译器可以将整个程序编译为一个单元,如果被问到,这在理论上会导致更多的优化可能性,并且因为它开始时是一个值得怀疑的优化,所以我真的对为了性能目的而在函数头中放置函数持怀疑态度
这种批评特别适用于OP在头文件中的“大”静态函数的例子。除非常量参数值允许编译器消除90%的代码或其他内容,否则大型函数几乎不可能从内联中受益。 (有关这种极端情况的实际示例,请参阅libavcodec
中使用的一些疯狂的内联函数/宏定义。: - )
答案 1 :(得分:9)
根据经验,您不应该在头文件中放置静态函数。在一次性程序中,除了扩展代码的大小之外,它可能不会伤害任何东西,因为你在每个模块中都有一个冗余副本。在共享库中,它很容易导致错误,因为现在您的库的一部分嵌入在库的调用者中,因此很容易发生版本不匹配。
如果你有一些非常可怕的时间关键函数,其中使函数调用花费的时间很重要,你可以考虑将它放在标题中,但在这种情况下(a)你可能想要将它声明为内联好吧,(b)你已经完成了所有其他优化工作。
简而言之,除非你知道在头文件中需要静态函数,否则你不需要在头文件中使用静态函数;你想要一个.c文件中的非静态函数,其标题在.h。
中答案 2 :(得分:8)
根据我的经验,在.h文件中定义一个函数通常是一个坏主意,而且我从来没有理由这样做,偶然这样做会导致我没有结束头痛。
虽然我猜它会允许包含标题的每个文件都有自己的单独的函数实现,如果函数具有静态变量,则可能是所需的行为,例如:如果你想/需要分别跟踪每个文件的一些信息。
答案 3 :(得分:2)
将静态工作缓冲区的函数定义为每个转换单元的本地函数也很有用。一个特定的例子是strtok()。 strtok()通过缓冲区每次调用一个令牌。如果strtok()调用从两个不同的位置(即两个不同的翻译单元)交错,则结果不是预期/期望的结果。如果每个翻译单元都有自己的strtok()副本,因此每个翻译单元都有自己的strtok()静态变量,那么这种对内部状态的踩踏就会消失。如果发生状态踩踏,则两个(组)调用都在同一个转换单元中,并且调试具有一些相似的局部性。
(注意,“正确”的解决方案是用无状态函数替换strtok()并使调用者负责保存上下文和状态信息,就像fopen()和朋友让调用者为每个上下文保存一个FILE一样。)
答案 4 :(得分:2)
Modern C采用了C ++中的inline
关键字来完成这样的任务。但是如果你的编译器没有(但是?)头文件中的static
是一种模仿它的方法。 inline
并不意味着函数必须内联到任何调用者,而只是在最终可执行文件中通常最多只有一个副本。 (从技术上讲,相应的链接符号是“弱”符号。)相反,如果只是声明static
,每个编译单元都会保留一个副本。
这种在头文件中使用函数定义的方法应限于 small 函数,这些函数执行小任务,如果编译器优化到调用函数中,编译器可能会大大改善代码。
这样做时,要小心这些功能的实现。您可能会破坏将声明包含在C ++中的可能性。一般来说,这两种语言(大多数)只在接口上达成一致,不一定是为了实现,存在细微差别。
答案 5 :(得分:-1)
如果函数具有外部链接,则应在.h文件中声明。
如果函数是静态的,因此没有外部链接,则该函数只应在定义它的.c文件中声明。
在头文件中定义函数永远不行。