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 f1f1function的地址

000000000000068c f2f2(staticc)函数的地址

注意function地址的区别,意味着什么。 对于一个声明了不同地址的函数来说,它可以很好的表示f2在很远的地方或者在一个不同的目标文件中。

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

现在认为GOT和PLT神奇地绑定了所有地址,dynamic部分保存了链接器可以看到的所有这些函数的信息。

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

dynamic部分仅保存地址0000000000000675处的f1函数的入口,而不保存f2

Num:数值大小types绑定Vis Ndx名称

  9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1 

这就是它! 从这里可以清楚地看到,链接器将不会成功findf2函数,因为它不在.so文件的dynamic部分。