C 语言中的静态函数

在 C 语言中使函数静态的意义是什么?

129739 次浏览

创建一个函数 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;
}

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 正是关于封装的; 除了向其他翻译单元隐藏函数(或者更确切地说,向其中的 因为隐藏函数)之外,使函数 static在编译器优化的情况下也能提供性能优势。

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

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

C 中的 static关键字用于编译后的文件(。与... 相反。H) ,以便函数只存在于该文件中。

通常,当您创建一个函数时,编译器会生成 cruft,链接器可以用它来将一个函数调用链接到该函数。如果使用 static 关键字,同一文件中的其他函数可以调用这个函数(因为不需要求助于链接器就可以完成) ,而链接器没有任何信息可以让其他文件访问这个函数。

程序员使用静态属性隐藏模块中的变量和函数声明, 就像在 Java 和 C + + 中使用公共和私有声明一样 用 static 属性声明的任何全局变量或函数都是该模块的私有属性。 类似地,任何没有静态属性声明的全局变量或函数都是公共的,并且可以是 保护变量和函数是很好的编程实践 尽可能使用静态属性。

看看上面的帖子,我想给出一个更明确的答案:

假设我们的 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 -o main
    

    会成功的!这是因为在 main.c文件包含 header.h之后,静态函数定义将位于相同的 main.c文件中(更准确地说,位于相同的转换单元中) ,直到被调用。

    如果运行 ./main,输出将是 Calling function inside header,这是静态函数应该打印的内容。

  • 案例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.c -o main
    

    会出错。在这种情况下,main.c只包含静态函数的声明,但是定义保留在另一个翻译单元中,并且 static关键字阻止定义要链接的函数的代码

  • 案例3:

    类似于情况2,只不过现在我们的头 header.h文件是:

      void FunctionInHeader(); // keyword static removed
    

    然后执行与案例2相同的命令,进一步执行 ./main将得到预期的结果。这里的 FunctionInHeader定义在另一个翻译单元中,但是定义它的代码可以链接。

因此,总结一下:

static keyword prevents the code defining a function to be linked,
when that function is defined in another translation unit than where it is called.

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

下面的内容是在将.so 文件转储到某个 人类可读性中之后的内容

000000000000675

00000000000068c

注意函数地址的不同,它意味着一些东西。对于使用不同地址声明的函数,它可以很好地表明 f2位于非常远的地方,或者位于对象文件的不同部分。

链接器使用 PLT (过程链接表)和 GET (全局偏移量表)来理解他们可以链接到的符号。

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

类的动态部分转储之后。所以文件我们得到了一些条目,但只对 F1F2函数感兴趣。

动态部分只保存地址为 0000000000675F1函数的条目,而不保存 F2的条目!

值大小类型绑定 Vis Ndx 名称

 9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1

就是这样!.由此可见,链接器将不能找到 F2函数,因为它不在。那就归档吧。

当需要限制对某些函数的访问时,我们将在定义和声明函数时使用 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 */