首页 文章

“静态”在C中意味着什么?

提问于
浏览
917

我在C代码的不同地方看到了 static 这个词;这就像C#中的静态函数/类(其中实现是跨对象共享的)?

18 回答

  • 33

    如果在函数static中声明一个变量,它的值将不会存储在函数调用堆栈中,并且在再次调用该函数时仍然可用 .

    如果声明全局变量static,则其范围将限制在您声明它的文件中 . 这比常规全局更安全,可以在整个程序中读取和修改 .

  • 3

    在C中,静态有两个含义,具体取决于其使用范围 . 在全局范围中,当在文件级别声明对象时,意味着该对象仅在该文件中可见 .

    在任何其他范围,它声明一个对象,该对象将在输入特定范围的不同时间之间保留其值 . 例如,如果在过程中对del进行了delcared:

    void procedure(void)
    {
       static int i = 0;
    
       i++;
    }
    

    在第一次调用该过程时,'i'的值被初始化为零,并且每次调用该过程时都会保留该值 . 如果'i'被打印,它将输出0,1,2,3 ......的序列...

  • 206

    静态变量是一个可以在函数中使用的特殊变量,它在调用之间保存数据,并且不会在调用之间删除它 . 例如:

    void func(){
        static int count; // If you don't declare its value, the value automatically initializes to zero
        printf("%d, ", count);
        count++;
    }
    
    void main(){
        while(true){
            func();
        }
    }
    

    输出:

    0,1,2,3,4,5 ......

  • 1288

    另请注意 static 可以以4种不同的方式使用 .

    to create permanent storage for local variables in a function.
    to specify internal linkage.
    to declare member functions that act like non-member functions.
    to create a single copy of a data member.
    
  • 1

    Multi-file variable 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
    */
    /*int i = 0;*/
    
    /* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
    /*int i;*/
    
    /* OK: extern. Will use the one in main. */
    extern int i;
    
    /* OK: only visible to this file. */
    static int si = 0;
    
    void a() {
        i++;
        si++;
        puts("a()");
        printf("i = %d\n", i);
        printf("si = %d\n", si);
        puts("");
    }
    

    main.c

    #include <stdio.h>
    
    int i = 0;
    static int si = 0;
    
    void a();    
    
    void m() {
        i++;
        si++;
        puts("m()");
        printf("i = %d\n", i);
        printf("si = %d\n", si);
        puts("");
    }
    
    int main() {
        m();
        m();
        a();
        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

    m()
    i = 1
    si = 1
    
    m()
    i = 2
    si = 2
    
    a()
    i = 3
    si = 1
    
    a()
    i = 4
    si = 2
    

    Interpretation

    • si 有两个独立的变量,每个文件一个

    • i 有一个共享变量

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

    在C编程中,文件通常用于表示"classes",而 static 变量表示类的私有静态成员 .

    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 i = 0;
    static int si = 0;
    

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

    readelf -s main.o
    

    输出包含:

    Num:    Value          Size Type    Bind   Vis      Ndx Name
      5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si
     10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i
    

    所以绑定是它们之间唯一的重要区别 . 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 进行优化,则 si 符号将完全从符号表中删除:无论如何都无法从外部使用它 . TODO为什么在没有优化时根据符号表保留静态变量?他们可以用于任何事情吗?也许是为了调试 .

    See also

    Try it yourself

    Example on github给你玩 .

  • 6

    我不想回答一个老问题,但我认为没有人提到K&R在“C程序设计语言”的A4.1节中如何解释它 .

    简而言之,单词static与 two 含义一起使用:

    • Static是两个存储类之一(另一个是自动存储类) . 静态对象在调用之间保持其值 . 在所有块之外声明的对象始终是静态的,不能自动生成 .

    • 但是,当 static keyword (强调它在代码中用作关键字)与声明一起使用时,它会为该对象提供内部链接,因此它只能在该转换单元中使用 . 但是如果关键字在函数中使用,它会更改对象的存储类(该对象只能在该函数中可见) . 与static相反的是 extern 关键字,它为对象提供外部链接 .

    Peter Van Der Linden在“专家C编程”中给出了这两个含义:

    • 在函数内部,在调用之间保留其值 .

    • 在功能级别,仅在此文件中可见 .

  • 12

    这取决于:

    int foo()
    {
       static int x;
       return ++x;
    }
    

    该函数将返回1,2,3等 . ---变量不在堆栈上 .

    a.c:

    static int foo()
    {
    }
    

    这意味着此函数仅在此文件中具有范围 . 所以a.c和b.c可以有不同的 foo() ,而foo不会暴露给共享对象 . 因此,如果您在a.c中定义了foo,则无法从 b.c 或任何其他位置访问它 .

    在大多数C库中,所有“私有”函数都是静态的,而大多数“公共”函数则不是 .

  • 5

    C中的静态变量具有程序的生命周期 .

    如果在函数中定义,它们具有局部范围,即只能在这些函数内访问它们 . 函数调用之间保留静态变量的值 .

    例如:

    void function()
    {
        static int var = 1;
        var++;
        printf("%d", var);
    }
    
    int main()
    {
        function(); // Call 1
        function(); // Call 2
    }
    

    在上述程序, var 存储在数据段中 . 它的生命周期是整个C程序 .

    函数调用1后, var 变为2.函数调用2后, var 变为3 .

    函数调用之间不会销毁 var 的值 .

    如果 var 介于非静态变量和局部变量之间,则它将存储在C程序的堆栈段中 . 由于函数的堆栈帧在函数返回后被销毁,因此 var 的值也被销毁 .

    初始化的静态变量存储在C程序的数据段中,而未初始化的静态变量存储在BSS段中 .

    关于static的另一个信息:如果变量是全局变量和静态变量,它具有C程序的生命周期,但它具有文件范围 . 它仅在该文件中可见 .

    试试这个:

    file1.c

    static int x;
    
    int main()
    {
        printf("Accessing in same file%d", x):
    }
    

    file2.c

    extern int x;
        func()
        {
            printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
        }
    
    run gcc -c file1.c
    
    gcc -c file2.c
    

    现在尝试使用以下方法链接它们

    gcc -o output file1.o file2.o
    

    它会产生链接器错误,因为x具有file1.c的文件范围,并且链接器将无法解析对file2.c中使用的变量x的引用 .

    参考文献:

  • 2

    来自维基百科:

    在C编程语言中,static与全局变量和函数一起使用,以将其范围设置为包含文件 . 在局部变量中,static用于将变量存储在静态分配的内存中,而不是自动分配的内存中 . 虽然该语言没有规定任何类型的存储器的实现,但是静态分配的存储器通常在编译时保留在程序的数据段中,而自动分配的存储器通常被实现为瞬态调用栈 .

  • 3

    静态变量值在不同的函数调用之间持续存在,并且范围仅限于本地块,静态var始终使用值0初始化

  • 12

    简短回答...... it depends.

    • 静态定义的局部变量在函数调用之间不会丢失它们的值 . 换句话说,它们是全局变量,但是作用于它们定义的局部函数 .

    • 静态全局变量在定义它们的C文件之外是不可见的 .

    • 静态函数在定义它们的C文件之外是不可见的 .

  • 4
    • 函数内的静态变量在调用之间保持其值 .

    • 静态全局变量或函数仅在声明的文件中为"seen"

    如果你是新手,(1)是更多的外国话题,所以这是一个例子:

    #include <stdio.h>
    
    void foo()
    {
        int a = 10;
        static int sa = 10;
    
        a += 5;
        sa += 5;
    
        printf("a = %d, sa = %d\n", a, sa);
    }
    
    
    int main()
    {
        int i;
    
        for (i = 0; i < 10; ++i)
            foo();
    }
    

    这打印:

    a = 15, sa = 15
    a = 15, sa = 20
    a = 15, sa = 25
    a = 15, sa = 30
    a = 15, sa = 35
    a = 15, sa = 40
    a = 15, sa = 45
    a = 15, sa = 50
    a = 15, sa = 55
    a = 15, sa = 60
    

    这对于函数需要在调用之间保持某种状态并且您不想使用全局变量的情况很有用 . 但要注意,应该非常谨慎地使用此功能 - 它使您的代码不是线程安全的,更难理解 .

    (2)广泛用作"access control"功能 . 如果您有.c文件实现某些功能,它通常只向用户公开几个"public"函数 . 其余的功能应该是 static ,这样用户将无法访问它们 . 这是封装,一个很好的做法 .

    引用Wikipedia

    在C编程语言中,static与全局变量和函数一起使用,以将其范围设置为包含文件 . 在局部变量中,static用于将变量存储在静态分配的内存中,而不是自动分配的内存中 . 虽然该语言没有规定任何类型的存储器的实现,但是静态分配的存储器通常在编译时保留在程序的数据段中,而自动分配的存储器通常被实现为瞬态调用栈 .

    有关详细信息,请参阅herehere .

    并回答你的第二个问题,它不像在C#中 .

    但是,在C中, static 也用于定义类属性(在同一类的所有对象之间共享)和方法 . 在C中没有类,所以这个功能是无关紧要的 .

  • 1

    重要的是要注意函数中的静态变量在第一次进入该函数时初始化,并且即使在它们的调用完成之后仍然存在; in case of recursive functions the static variable gets initialized only once and persists as well over all recursive calls and even after the call of the function has been finished.

    如果变量是在函数外部创建的,则意味着程序员只能使用已声明变量的源文件中的变量 .

  • 19

    人们一直说C中的“静态”有两个含义 . 我提供了一种查看它的另一种方式,它具有单一含义:

    • 将'static'应用于某个项目会强制该项目具有两个属性:(a)它在当前范围之外不可见; (b)它是持久的 .

    它似乎有两个含义的原因是,在C中,可以应用'static'的每个项目都具有这两个属性中的一个,所以看起来好像这个特定用法只涉及另一个 .

    例如,考虑变量 . 在函数之外声明的变量已经具有持久性(在数据段中),因此应用'static'只能使它们在当前范围之外不可见(编制单位) . 相反,在函数内部声明的变量已经在当前作用域(函数)之外具有不可见性,因此应用“静态”只能使它们具有持久性 .

    将'static'应用于函数就像将它应用于全局变量一样 - 代码必须是持久的(至少在语言中),因此只能改变可见性 .

    注意:这些注释仅适用于C.在C中,将'static'应用于类方法确实赋予关键字不同的含义 . 类似地,对于C99数组参数扩展 .

  • 2

    static 在不同的上下文中表示不同的东西 .

    • 您可以在C函数中声明静态变量 . 此变量仅在函数中可见,但它的行为类似于全局变量,因为它仅初始化一次并保留其值 . 在此示例中,每次调用 foo() 时,它将打印一个递增的数字 . 静态变量只初始化一次 .
    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
    
    • 静态的另一个用途是在.c文件中实现函数或全局变量但不希望其符号在文件生成的 .obj 之外可见 . 例如
    static void foo() { ... }
    
  • 5

    有2种情况:

    (1)声明的局部变量 static :分配在数据段而不是堆栈中 . 当您再次调用该函数时,其值将保留 .

    (2)声明的全局变量或函数 static :不可见的外部编译单元(即链接期间符号表中的局部符号) .

  • 148

    如果你在mytest.c文件中声明:

    static int my_variable;
    

    然后,只能从此文件中看到此变量 . 该变量无法在其他任何地方导出 .

    如果在函数内部声明,则每次调用函数时,变量的值都将保持其值 .

    无法从文件外部导出静态函数 . 因此,在* .c文件中,如果将它们声明为静态,则隐藏函数和变量 .

  • 48

    还有一个用途未在此处介绍,它作为数组类型声明的一部分作为函数的参数:

    int someFunction(char arg[static 10])
    {
        ...
    }
    

    在此上下文中,它指定传递给此函数的参数必须是 char 类型的数组,其中至少包含10个元素 . 有关详细信息,请参阅我的问题here .

相关问题