C中的静态函数
在C中静态函数有什么意义?
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; }
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; }
pmg是关于封装的地方; 除了隐藏其他翻译单元的function(或者说, 因为它),使得functionstatic
也可以在存在编译器优化的情况下赋予性能优点。
由于static
函数不能从当前翻译单元以外的任何地方调用(除非代码需要一个指向其地址的指针),编译器将控制所有的调用点。
这意味着它可以自由使用非标准的ABI,完全内联,或者执行任何其他优化,这些优化对于具有外部链接的function来说可能是不可能的。
C中的static
关键字用在编译的文件(.c而不是.h)中,以便该函数仅存在于该文件中。
通常,当你创build一个函数时,编译器会生成链接器可以使用的cruft,并且将函数调用链接到该函数。 如果使用static关键字,则同一个文件中的其他函数可以调用这个函数(因为它可以在不使用链接器的情况下完成),而链接器没有让其他文件访问函数的信息。
看看上面的post,我想指出一个细节。
假设我们的主文件(“main.c”)如下所示:
#include "header.h" int main(void) { FunctionInHeader(); }
现在考虑三种情况:
-
案例1:我们的头文件(“header.h”)如下所示:
#include <stdio.h> static void FunctionInHeader(); void FunctionInHeader() { printf("Calling function inside header\n"); }
然后在linux上执行以下命令:
gcc main.c header.h -o main
会成功 ! 之后,如果一个运行
./main
输出将是
调用header中的函数
这是什么静态function应该打印。
-
情况2:我们的头文件(“header.h”)如下所示:
static void FunctionInHeader();
而且我们还有一个文件“header.c”,看起来像这样:
#include <stdio.h> #include "header.h" void FunctionInHeader() { printf("Calling function inside header\n"); }
然后下面的命令
gcc main.c header.h header.c -o main
会给出错误。
-
案例3:
类似于情况2,除了现在我们的头文件(“header.h”)是:
void FunctionInHeader(); // keyword static removed
然后,与情况2相同的命令将成功,并进一步执行./main将给出预期的结果。
所以从这些testing(在macros碁x86机器,Ubuntu操作系统上执行),我做了一个假设
静态关键字防止函数在另一个文件中定义,而不是声明它的位置。
纠正我,如果我错了。
C程序员使用static属性来隐藏模块中的variables和函数声明,就像在Java和C ++中使用公共和私有声明一样。 C源文件扮演模块的angular色。 用静态属性声明的全局variables或函数对于该模块是私有的。 类似地,任何声明没有静态属性的全局variables或函数都是公共的,并且可以被任何其他模块访问。 尽可能地用静态属性保护你的variables和函数是很好的编程习惯。
pmg的回答非常有说服力。 如果你想知道静态声明如何在对象级别工作,那么下面的这些信息可能对你很有意思。 我重用了由pmg编写的相同程序,并将其编译到.so(共享对象)文件中
以下内容是将.so文件转储为人类可读的内容之后
0000000000000675 f1 : f1function的地址
000000000000068c f2 : f2(staticc)函数的地址
注意function地址的区别,意味着什么。 对于一个声明了不同地址的函数来说,它可以很好的表示f2在很远的地方或者在一个不同的目标文件中。
链接器使用称为PLT(过程链接表)和GOT(全局偏移量表)的东西来理解他们有权访问链接的符号。
现在认为GOT和PLT神奇地绑定了所有地址,dynamic部分保存了链接器可以看到的所有这些函数的信息。
转储.so文件的dynamic部分后,我们得到了一堆条目,但只对f1和f2函数感兴趣。
dynamic部分仅保存地址0000000000000675处的f1函数的入口,而不保存f2 !
Num:数值大小types绑定Vis Ndx名称
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
这就是它! 从这里可以清楚地看到,链接器将不会成功findf2函数,因为它不在.so文件的dynamic部分。