C中的静态函数


Answers:


212

生成函数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;
}

8
翻译单位是此处使用的正确术语吗?目标文件会更准确吗?据我了解,链接器隐藏了一个静态函数,并且该链接器不在翻译单元上运行。
Steven Eckhoff 2014年

2
我还应该说,我想认为它是隐藏在链接器中的;这样看起来更清晰。
Steven Eckhoff 2014年

1
因此,对于内部函数(确保不要在其c文件之外调用它),我们应该将其作为静态函数使用,对吗?因此,我们可以确定它无法在其他地方调用。谢谢:)
hqt 2014年

1
您如何编译呢?你用#include <helper_file.c>吗?我认为那将使它成为一个翻译单位……
Atcold '16

2
@Atcold:我编写代码的方式只是在命令行中包含了两个源文件,就像这样gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c。这些函数的原型都存在于两个源文件中(不需要头文件)。链接器将解析功能。
pmg

80

pmg关于封装问题。除了将功能隐藏在其他翻译单元之外(或者因为它而已)之外,制作功能static还可以在存在编译器优化的情况下提高性能。

因为static不能从当前翻译单元之外的任何地方调用函数(除非代码使用指向其地址的指针),所以编译器控制进入它的所有调用点。

这意味着可以自由使用非标准的ABI,完全内联它或执行许多其他优化,而这些优化可能对于具有外部链接的功能是不可能的。


9
...除非函数的地址被占用。
CAF

1
@caf函数的地址是什么意思?对我来说,在编译时具有地址或被分配地址的函数/变量的概念有点令人困惑。您能详细说明一下吗?
2013年

2
@crypticcoder:您的程序已加载到内存中,因此函数也具有一个内存位置,并且可以获取地址。使用函数指针,您可以调用任何一个。如果这样做,则会减少编译器可执行的优化列表,因为代码必须完整保留在同一位置。

5
@crypticcoder:我的意思是一个表达式求值指向该函数的指针,并用它做一些事,而不是立即调用该函数。如果指向某个static函数的指针转义了当前的翻译单元,则可以直接从其他翻译单元调用该函数。
CAF

@caf如果使用了函数的地址,编译器是否会检测到并关闭此答案中提到的静态函数优化(例如,使用非标准ABI)?我想那是必须的。
塞夫科

28

staticC中关键字在编译文件(.c相对于.h)中使用,因此该功能仅存在于该文件中。

通常,当您创建函数时,编译器会生成链接器可用来将函数调用链接到该函数的文件。如果使用static关键字,则同一文件中的其他函数可以调用此函数(因为无需使用链接器即可完成此功能),而链接器没有信息可让其他文件访问该函数。


1
3Doub:使用“ Cruft”一词比您认为的要精确。在问题的上下文中,“拼写”是在此处使用的正确词。
Erik Aronesty 2014年

@ 3Doubloons我同意它是简化的,但是我认为这对于初学者来说更容易理解。
IngoBürk'15

11

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

假设我们的主文件(“ 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

    输出将是

    标头中的调用函数

    静态函数应该打印什么。

  • 情况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将给出预期的结果。

因此,根据这些测试(在Acer x86计算机,Ubuntu OS上执行),我假设

static关键字防止函数在定义位置以外的另一个* .c文件中调用。

如果我错了,请纠正我。


5

C程序员使用static属性在模块内部隐藏变量和函数声明,就像在Java和C ++中使用公共和私有声明一样。C源文件充当模块的角色。用static属性声明的任何全局变量或函数对该模块都是私有的。同样,声明为没有static属性的任何全局变量或函数都是公共的,并且可以由任何其他模块访问。良好的编程习惯是尽可能使用static属性保护变量和函数。


4

pmg的答案非常令人信服。如果您想知道静态声明在对象级别的工作方式,那么下面的信息对您可能很有趣。我重用了pmg编写的相同程序,并将其编译为.so(共享对象)文件

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

0000000000000675 f1f1函数的地址

000000000000068c f2f2(staticc)函数的地址

注意函数地址的差异,表示某种含义。对于用不同地址声明的函数,它可以很好地表示f2位于很远的地方或位于目标文件的不同段中。

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

现在,以为GOT和PLT神奇地绑定了所有地址,而动态节则保存了链接器可见的所有这些功能的信息。

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

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

编号:值大小类型绑定Vis Ndx名称

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

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


0

当需要限制对某些函数的访问时,我们将在定义和声明函数时使用static关键字。

            /* file ab.c */ 
static void function1(void) 
{ 
  puts("function1 called"); 
} 
And store the following code in another file ab1.c

/* file ab1.c  */ 
int main(void) 
{ 
 function1();  
  getchar(); 
  return 0;   
} 
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */

这个答案不是很有帮助。
fiscblog
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.