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

时间:2009-02-21 06:47:52

标签: c syntax static

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

19 个答案:

答案 0 :(得分:1361)

  1. 函数内的静态变量在调用之间保持其值。
  2. 静态全局变量或函数仅在
  3. 中声明的文件中“看到” 如果你是新手,

    (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)广泛用作“访问控制”功能。如果您有.c文件实现某些功能,它通常只向用户公开一些“公共”功能。其余的功能应该是static,这样用户就无法访问它们。这是封装,是一种很好的做法。

    引用Wikipedia

      

    在C编程语言中,静态   用于全局变量和   函数将其范围设置为   包含文件。在局部变量中,   static用于存储变量   在静态分配的内存中   而不是自动分配   记忆。虽然语言没有   决定执行任何一个   内存类型,静态分配   内存通常保留在数据中   编译时的程序段   时间,而自动   分配内存通常是   实现为瞬态调用堆栈。

    有关详细信息,请参阅herehere

    回答你的第二个问题,它与C#不同。

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

答案 1 :(得分:212)

此处未涉及另外一个用法,它作为数组类型声明的一部分作为函数的参数:

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

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

答案 2 :(得分:153)

简短回答...... 取决于。

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

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

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

答案 3 :(得分:50)

多文件变量范围示例

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

交流转换器

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

GitHub upstream

编译并运行:

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

输出:

m()
i = 1
si = 1

m()
i = 2
si = 2

a()
i = 3
si = 1

a()
i = 4
si = 2

<强>解释

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

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

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

标准说什么

C99 N1256 draft 6.7.1“存储类说明符”表示static是“存储类说明符”。

6.2.2 / 3“标识符链接”说static暗示internal linkage

  

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

和6.2.2 / 2表示internal linkage的行为类似于我们的示例:

  

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

其中“翻译单位是预处理后的源文件。

GCC如何为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为什么在没有优化时根据符号表保留静态变量?他们可以用于任何事情吗?也许是为了调试。

另见

C ++匿名命名空间

在C ++中,您可能希望使用匿名命名空间而不是静态命名空间,这会产生类似的效果,但会进一步隐藏类型定义:Unnamed/anonymous namespaces vs. static functions

答案 4 :(得分:34)

取决于:

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

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

交流转换器:

static int foo()
{
}

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

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

答案 5 :(得分:19)

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

  • 对项目应用'static'会强制该项具有两个属性:(a)在当前范围之外不可见; (b)坚持不懈。

它似乎有两个含义的原因是,在C中,可以应用“静态”的每个项目已经具有这两个属性中的一个,所以似乎好像该特定用法仅涉及另一个。

例如,考虑变量。在函数之外声明的变量已经具有持久性(在数据段中),因此应用'static'只能使它们在当前作用域(编译单元)之外不可见。相反,在函数内部声明的变量已经在当前作用域(函数)之外具有不可见性,因此应用'static'只能使它们持久化。

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

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

答案 6 :(得分:13)

来自维基百科:

  

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

答案 7 :(得分:13)

static在不同的背景下意味着不同的东西。

  1. 您可以在C函数中声明一个静态变量。此变量仅在函数中可见,但它的行为类似于全局变量,因为它仅初始化一次并保留其值。在此示例中,每次调用foo()时,它都会打印越来越多的数字。静态变量只初始化一次。

    void foo ()
    {
    static int i = 0;
    printf("%d", i); i++
    }
    
  2. 静态的另一个用途是在.c文件中实现函数或全局变量,但不希望其符号在文件生成的.obj之外可见。 e.g。

    static void foo() { ... }
    

答案 8 :(得分:6)

我讨厌回答一个老问题,但我认为没有人提及K&amp; R如何在C#编程语言&#34;的第A4.1节中解释它。

简而言之,static一词用于两个含义:

  1. Static是两个存储类之一(另一个是 自动)。静态对象在调用之间保持其值。在所有块之外声明的对象始终是静态的,不能自动生成。
  2. 但是,当static 关键字时(非常强调它被用于 代码作为关键字)与声明一起使用,它为该对象提供内部链接,因此它只能在该转换单元中使用。但是如果关键字在函数中使用,它会更改对象的存储类(该对象只能在该函数中可见)。与static相反的是extern关键字,它为对象提供外部链接。
  3. Peter Van Der Linden在&#34; Expert C Programming&#34;中提供了这两个含义:

    • 在函数内部,在调用之间保留其值。
    • 在功能级别,仅在此文件中可见。

答案 9 :(得分:5)

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

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

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

   i++;
}

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

的序列

答案 10 :(得分:5)

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

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

答案 11 :(得分:4)

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

static int my_variable;

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

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

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

答案 12 :(得分:3)

重要的是要注意函数中的静态变量在第一次进入该函数时初始化,并且即使在它们的调用完成后仍然存在; 在递归函数的情况下,静态变量只被初始化一次,并且在所有递归调用之后仍然存在,甚至在函数调用完成之后也是如此。

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

答案 13 :(得分:3)

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的引用,则会产生链接器错误。

参考文献:

  1. http://en.wikipedia.org/wiki/Translation_unit_(programming)
  2. http://en.wikipedia.org/wiki/Call_stack

答案 14 :(得分:2)

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

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 ......

答案 15 :(得分:1)

静态变量值在不同的函数调用之间持续存在,其范围仅限于本地块 一个静态var总是用值0初始化

答案 16 :(得分:1)

有两种情况:

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

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

答案 17 :(得分:1)

即使静态变量超出其范围,其静态属性仍会保持其值!因此,静态变量会将其先前的值保留在先前的范围内,而不会在新的范围内再次初始化。

例如看这个- 程序运行时,静态int变量保留在内存中。当声明了该变量的函数调用结束时,普通或自动变量将被破坏。

#include<stdio.h> 
int fun() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("%d ", fun()); 
  printf("%d ", fun()); 
  return 0; 
}

这将输出:1 2

由于1声明为静态,因此保留在内存中

如果未明确初始化静态变量(如全局变量),则初始化为0。例如,在下面的程序中,x的值打印为0,而y的值则是垃圾。有关更多详细信息,请参见此。

#include <stdio.h> 
int main() 
{ 
    static int x; 
    int y; 
    printf("%d \n %d", x, y); 
}

这将输出:0 [some_garbage_value]

这些是我发现的主要功能,上面没有为新手做过解释!

答案 18 :(得分:0)

在C编程中,static是保留关键字,它可以控制生存期和可见性。如果我们将变量声明为函数内部的静态变量,则该变量仅在整个函数中可见。在这种用法中,此静态变量的生存期将在函数调用时开始,并在执行该函数后销毁。您可以看到以下示例:

#include<stdio.h> 
int counterFunction() 
{ 
  static int count = 0; 
  count++; 
  return count; 
} 

int main() 
{ 
  printf("First Counter Output = %d\n", counterFunction()); 
  printf("Second Counter Output = %d ", counterFunction()); 
  return 0; 
}

以上程序将为我们提供此输出:
第一计数器输出= 1
秒计数器输出= 1
因为一旦调用该函数,它将初始化count =0。并且在执行counterFunction时,它将破坏count变量。