【问题标题】:static function in CC中的静态函数
【发布时间】:2011-07-16 05:12:29
【问题描述】:

在 C 中将函数设为静态有什么意义?

【问题讨论】:

  • @nightcracker:C++ 中没有“方法”之类的东西。我认为您对 Objective-C 感到困惑。
  • 不,我对 Python 感到困惑。类中的函数在 Python 中称为方法。

标签: c static


【解决方案1】:

创建函数 static 会将其隐藏在其他翻译单元中,这有助于提供 encapsulation

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;
}

ma​​in.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;
}

【讨论】:

  • 翻译单元是在这里使用的正确术语吗?目标文件不是更准确吗?据我了解,静态函数对链接器是隐藏的,并且链接器不对翻译单元进行操作。
  • 我还应该说,我喜欢将其视为对链接器隐藏的;这样看起来更清楚了。
  • 所以,内部函数(我们肯定不会在其 c 文件之外调用它),我们应该把它作为静态函数,对吧?所以,我们可以确定它不能在其他地方调用。谢谢:)
  • 你如何编译这个?你用#include <helper_file.c>吗?我认为这将使它成为一个单一的翻译单元......
  • @Atcold:按照我编写代码的方式,您只需在命令行中包含 2 个源文件,例如 gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c。函数的原型存在于两个源文件中(不需要头文件)。链接器将解析函数。
【解决方案2】:

pmg 是关于封装的;除了对其他翻译单元隐藏函数(或者更确切地说,因为它),使函数static 还可以在编译器优化的情况下赋予性能优势。

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

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

【讨论】:

  • ...除非函数的地址被占用。
  • @caf 函数地址被占用是什么意思?对我来说,在编译时具有地址或分配地址的函数/变量的概念有点令人困惑。能详细点吗?
  • @crypticcoder:你的程序加载到内存中,因此函数也有内存位置,可以获取地址。使用函数指针,您可以调用其中的任何一个。如果你这样做,它会减少编译器可以执行的优化列表,因为代码必须保持原样。
  • @crypticcoder:我的意思是一个表达式计算一个指向函数的指针并用它做一些事情,而不是立即调用函数。如果指向static 函数的指针转义当前翻译单元,则可以直接从其他翻译单元调用该函数。
  • @caf 如果函数的地址被占用,编译器是否会检测到并关闭此答案中提到的静态函数优化(例如使用非标准 ABI)?我想它必须这样做。
【解决方案3】:

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

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

【讨论】:

  • 3Doub:使用“cruft”这个词比你想象的要准确。在问题的上下文中,“cruft”是在这里使用的正确词。
  • @3Doubloons 我同意它被简化了,但我认为这让初学者更容易理解。
【解决方案4】:

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

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

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

      之后

      0000000000000675 f1f1函数的地址

      000000000000068c f2f2(staticc)函数的地址

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

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

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

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

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

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

       9: 0000000000000675    23 FUNC    GLOBAL DEFAULT   11 f1
      

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

      【讨论】:

        【解决方案7】:

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

        【讨论】:

        • 这个答案不是很有帮助。
        猜你喜欢
        • 2013-12-20
        • 1970-01-01
        • 2021-09-10
        • 2011-07-18
        • 2011-05-06
        • 1970-01-01
        相关资源
        最近更新 更多