首页 文章

什么是“静态”功能?

提问于
浏览
423

问题是关于普通的c函数,而不是c++ static 方法,正如评论中所阐明的那样 .

好的,我明白 static 变量是什么,但是什么是 static 函数?

为什么如果我声明一个函数,让我们说 void print_matrix ,让我们说 a.c (没有 a.h )并包含 "a.c" - 我得到 "print_matrix@@....) already defined in a.obj" ,但是如果我把它声明为 static void print_matrix 那么它编译?

UPDATE 只是为了清理 - 我知道包括 .c 在内的很糟糕,正如你们许多人指出的那样 . 我只是暂时清除_462214中的空格,直到我更好地了解如何将所有这些功能分组到正确的 .h.c 文件中 . 只是一个临时的快速解决方案 .

10 回答

  • 8

    static 函数是仅对同一文件中的其他函数可见的函数(更准确地说是相同的translation unit) .

    EDIT :对于那些认为,问题的作者意味着'class method':因为问题标记为 C ,他的意思是一个普通的旧C函数 . 对于(C / Java / ...)类方法, static 表示可以在类本身上调用此方法,不需要该类的实例 .

  • 178

    Minimal runnable multi-file scope example

    这里我将说明 static 如何影响多个文件中的函数定义范围 .

    a.c

    #include <stdio.h>
    
    /* Undefined behavior: already defined in main.
     * Binutils 2.24 gives an error and refuses to link.
     * https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
     */
    /*void f() { puts("a f"); }*/
    
    /* OK: only declared, not defined. Will use the one in main. */
    void f(void);
    
    /* OK: only visible to this file. */
    static void sf() { puts("a sf"); }
    
    void a() {
        f();
        sf();
    }
    

    main.c

    #include <stdio.h>
    
    void a(void);        
    
    void f() { puts("main f"); }
    
    static void sf() { puts("main sf"); }
    
    void m() {
        f();
        sf();
    }
    
    int main() {
        m();
        a();
        return 0;
    }
    

    Compile

    gcc -c a.c -o a.o
    gcc -c main.c -o main.o
    gcc -o main main.o a.o
    

    Output

    main f
    main sf
    main f
    a sf
    

    Interpretation

    • 有两个单独的函数 sf ,每个文件一个

    • 有一个共享函数 f

    像往常一样,范围越小越好,所以如果可以,总是声明函数 static .

    在C编程中,文件通常用于表示"classes",而 static 函数表示类的"private"方法 .

    一个常见的C模式是将 this 结构作为第一个"method"参数传递,这基本上是C在引擎盖下的作用 .

    What standards say about it

    C99 N1256 draft 6.7.1 "Storage-class specifiers"说 static 是"storage-class specifier" .

    6.2.2 / 3 "Linkages of identifiers"说 static 暗示 internal linkage

    如果对象或函数的文件范围标识符的声明包含存储类说明符static,则标识符具有内部链接 .

    和6.2.2 / 2说 internal linkage 在我们的例子中表现得像:

    在构成整个程序的翻译单元和库的集合中,具有外部链接的特定标识符的每个声明表示相同的对象或功能 . 在一个翻译单元内,具有内部链接的标识符的每个声明表示相同的对象或功能 .

    其中“翻译单元”是预处理后的源文件 .

    How GCC implements it for ELF (Linux)?

    随着 STB_LOCAL 绑定 .

    如果我们编译:

    int f() { return 0; }
    static int sf() { return 0; }
    

    并用以下符号反汇编符号表:

    readelf -s main.o
    

    输出包含:

    Num:    Value          Size Type    Bind   Vis      Ndx Name
      5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
      9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f
    

    所以绑定是它们之间唯一的重要区别 . Value 只是它们偏移到 .bss 部分,因此我们预计它会有所不同 .

    STB_LOCAL 记录在http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html的ELF规范中:

    STB_LOCAL在包含其定义的目标文件外部,本地符号不可见 . 多个文件中可能存在同名的本地符号,而不会相互干扰

    这使它成为代表 static 的完美选择 .

    没有静态的函数是 STB_GLOBAL ,规范说:

    当链接编辑器组合多个可重定位目标文件时,它不允许多个具有相同名称的STB_GLOBAL符号定义 .

    这与多个非静态定义上的链接错误一致 .

    如果我们使用 -O3 进行优化,则 sf 符号将完全从符号表中删除:无论如何都无法从外部使用它 . TODO为什么在没有优化时根据符号表保留静态函数?他们可以用于任何事情吗?

    See also

    Try it yourself

    Example on GitHub供你玩 .

  • 18

    以下是关于普通C函数 - 在C类中,修饰符'static'具有另一种含义 .

    如果你只有一个文件,这个修饰符绝对没有区别 . 不同之处在于包含多个文件的更大项目:

    在C中,每个“模块”(sample.c和sample.h的组合)都是独立编译的,然后每个编译的目标文件(sample.o)由链接器链接到一个可执行文件 .

    假设您有几个文件包含在主文件中,其中两个文件只有一个内部用于方便的函数 add(int a, b) - 编译器可以轻松地为这两个模块创建目标文件,但链接器会抛出错误,因为它找到两个具有相同名称的函数,并且它不知道它应该使用哪个函数(即使在其他地方使用了's nothing to link, because they aren'但在它自己的文件中) .

    这就是为什么你使这个函数,它只用于内部,一个静态函数 . 在这种情况下编译器不为链接器创建典型的“你可以链接这个东西”-flag,这样链接器就不会看到这个函数,也不会产生错误 .

  • 7

    第一:在另一个文件中包含一个 .cpp 文件通常是一个坏主意 - 它会导致这样的问题:-)通常的方法是创建单独的编译单元,并为包含的文件添加头文件 .

    其次:

    C在这里有一些令人困惑的术语 - 直到评论中指出我才知道它 .

    a) static functions - 继承自C,你在这里谈论的是什么 . 任何课外 . 静态 function 表示它在当前编译单元外部不可见 - 因此在您的情况下,a.obj具有副本,而您的其他代码具有独立副本 . (使用代码的多个副本来膨胀最终的可执行文件) .

    b)static member function - 对象方向的术语是静态 method . 住在课堂里 . 您可以使用类而不是通过对象实例来调用它 .

    这两种不同的静态函数定义完全不同 . 小心 - 这里是龙 .

  • 586

    Minor nit:静态函数对于翻译单元是可见的,对于大多数实际情况,该函数是定义函数的文件 . 您获得的错误通常被称为违反单一定义规则 .

    标准可能会说:

    “每个程序应该只包含该程序中使用的每个非内联函数或对象的一个定义;不需要诊断 . ”

    这是查看静态函数的C方式 . 但是,这在C中已弃用 .

    另外,在C中,您可以将成员函数声明为static . 这些主要是元函数,即它们不描述/修改特定对象的行为/状态,而是作用于整个类本身 . 此外,这意味着您不需要创建一个对象来调用静态成员函数 . 此外,这也意味着,您只能从这样的函数中访问静态成员变量 .

    我将Parrot的例子添加到Singleton模式中,该模式基于这种静态成员函数来在程序的整个生命周期中获取/使用单个对象 .

  • 71

    静态函数的答案取决于语言:

    1)在没有像C这样的OOPS的语言中,这意味着该函数只能在其定义的文件中访问 .

    2)在像C这样的OOPS语言中,这意味着可以直接在类上调用该函数而无需创建它的实例 .

  • 6

    C中的静态函数与C中的静态成员函数之间存在很大差异 . 在C中,静态函数在其转换单元之外是不可见的,它是编译成的目标文件 . 换句话说,使函数静态限制其范围 . 您可以将静态函数视为其* .c文件的“私有”(尽管这不是严格正确的) .

    在C中,“static”也可以应用于类的成员函数和数据成员 . 静态数据成员也称为“类变量”,而非静态数据成员是“实例变量” . 这是Smalltalk术语 . 这意味着类的所有对象只共享一个静态数据成员的副本,而每个对象都有自己的非静态数据成员副本 . 因此,静态数据成员本质上是一个全局变量,它是一个类的成员 .

    非静态成员函数可以访问类的所有数据成员:静态和非静态 . 静态成员函数只能对静态数据成员进行操作 .

    考虑这一点的一种方法是在C静态数据成员和静态成员函数不属于任何对象,而是属于整个类 .

  • 13

    关于C中的函数,关键字static有两种用途 .

    第一种是将函数标记为具有内部链接,因此不能在其他翻译单元中引用它 . 此用法在C中已弃用 . 对于此用法,首选未命名的命名空间 .

    // inside some .cpp file:
    
    static void foo();    // old "C" way of having internal linkage
    
    // C++ way:
    namespace
    {
       void this_function_has_internal_linkage()
       {
          // ...
       }
    }
    

    第二种用法是在类的上下文中 . 如果一个类具有静态成员函数,则意味着该函数是该类的成员(并且具有对其他成员的通常访问权限),但不需要通过特定对象调用它 . 换句话说,在该函数内部,没有“this”指针 .

  • 14

    静态函数定义将此符号标记为内部 . 因此,从外部链接不可见,但只能看到同一编译单元中的函数,通常是同一个文件 .

  • 38

    静态函数是可以在类本身上调用的函数,而不是类的实例 .

    例如,非静态将是:

    Person* tom = new Person();
    tom->setName("Tom");
    

    此方法适用于类的实例,而不是类本身 . 但是,您可以使用静态方法,无需实例即可工作 . 有时在工厂模式中使用:

    Person* tom = Person::createNewPerson();
    

相关问题