C中的静态函数

Cen*_*noc 167 c static

在C中使函数静态有什么意义?

pmg*_*pmg 210

使函数static隐藏其他翻译单元,这有助于提供封装.

helper_file.c

int f1(int);        /* prototype */
static int f2(int); /* prototype */

int f1(int foo) {
    return f2(foo); /* ok, f2 is in the same translation unit */
                    /* (basically same .c file) as f1         */
}

int f2(int foo) {
    return 42 + foo;
}
Run Code Online (Sandbox Code Playgroud)

main.c:

int f1(int); /* prototype */
int f2(int); /* prototype */

int main(void) {
    f1(10); /* ok, f1 is visible to the linker */
    f2(12); /* nope, f2 is not visible to the linker */
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 翻译单元在这里使用的是正确的术语吗?对象文件不会更准确吗?据我所知,链接器隐藏了静态函数,链接器不能在转换单元上运行. (8认同)
  • 我也应该说,我喜欢将它视为隐藏链接器; 这样看起来更清楚. (2认同)
  • 你如何编译这个?你使用`#include <helper_file.c>`吗?我认为这将使它成为一个单一的翻译单元然后...... (2认同)
  • @Accold:我编写代码的方式只需在命令行中包含2个源文件,例如`gcc -std = c99 -pedantic -Wall -Wextra main.c helper_file.c`.这些函数的原型存在于两个源文件中(不需要头文件).链接器将解析这些功能. (2认同)
  • @mheiber:当您不声明函数“静态”时,即使没有原型,链接器也可以找到它。缺少原型会阻止编译器检查返回和参数类型,而不会发出调用该函数的代码。 (2认同)

Ste*_*non 79

pmg是关于封装的点; 除了将函数隐藏在其他转换单元之外(或者更确切地说,因为它),使函数static也可以在存在编译器优化的情况下提供性能优势.

因为static无法从当前转换单元之外的任何位置调用函数(除非代码采用指向其地址的指针),否则编译器会控制所有调用点.

这意味着它可以自由地使用非标准ABI,完全内联,或执行任何其他优化,这些优化对于具有外部链接的函数可能是不可能的.

  • ...除非获取功能的地址. (9认同)
  • @crypticcoder:我的意思是表达式计算一个指向函数的指针,并对它做一些事情,而不是立即调用该函数.如果指向`static`函数的指针转义当前转换单元,则可以从其他转换单元直接调用该函数. (5认同)
  • @crypticcoder:你的程序被加载到内存中,因此函数也有一个内存位置,可以获得地址.使用函数指针,您可以调用其中任何一个.如果这样做,它会减少编译器可以执行的优化列表,因为代码必须在同一位置保持不变. (2认同)

3Do*_*ons 28

staticC中的关键字用于编译文件(.c而不是.h),因此该函数仅存在于该文件中.

通常,当您创建一个函数时,编译器会生成链接器可以使用的cruft,以及将函数调用链接到该函数.如果使用static关键字,则同一文件中的其他函数可以调用此函数(因为它可以在不借助链接器的情况下完成),而链接器没有信息让其他文件访问该函数.

  • 3Doub:“cruft”这个词的使用比你想象的更准确。在问题的上下文中,“cruft”是在这里使用的正确词。 (2认同)

mer*_*114 9

看看上面的帖子,我想指出一个细节.

假设我们的主文件("main.c")如下所示:

#include "header.h"

int main(void) {
    FunctionInHeader();
}
Run Code Online (Sandbox Code Playgroud)

现在考虑三种情况:

所以从这些测试(在Acer x86机器上执行,Ubuntu OS)我做了一个假设

static关键字阻止在另一个文件中定义函数,而不是在声明它的位置.

如果我错了,请纠正我.


小智 5

C程序员使用static属性隐藏模块内的变量和函数声明,就像在Java和C++中使用公共声明和私有声明一样.C源文件扮演模块的角色.使用static属性声明的任何全局变量或函数对该模块都是私有的.类似地,没有静态属性声明的任何全局变量或函数都是公共的,并且可以由任何其他模块访问.使用静态属性尽可能保护变量和函数是一种很好的编程习惯.


hum*_*.js 5

pmg的回答很有说服力。如果您想知道静态声明如何在对象级别工作,那么下面的信息可能对您很有趣。我重用了由 pmg 编写的相同程序并将其编译成一个 .so(shared object) 文件

以下内容是在将 .so 文件转储为人类可读的内容之后

0000000000000675 f1 : f1 函数的地址

000000000000068c f2 : f2(staticc) 函数的地址

注意函数地址的不同,它意味着什么。对于使用不同 address 声明的函数,它可以很好地表示 f2 位于非常远的地方或位于目标文件的不同段中。

链接器使用称为 PLT(程序链接表)和 GOT(全局偏移表)的东西来理解他们有权链接到的符号。

现在想想 GOT 和 PLT 神奇地绑定了所有地址,一个动态部分保存了链接器可见的所有这些函数的信息。

在转储 .so 文件的动态部分后,我们得到了一堆条目,但只对f1f2函数感兴趣。

动态部分仅保存地址0000000000000675处的f1函数的条目,而不保存f2 的条目!

Num:值大小类型绑定可视 Ndx 名称

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1
Run Code Online (Sandbox Code Playgroud)

就是这样!。由此可见,链接器将无法成功找到f2函数,因为它不在 .so 文件的动态部分。