C中的“静态”是什么意思?

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

1099210 次浏览
  1. 函数内部的静态变量在两次调用之间保持其值。
  2. 静态全局变量或函数仅在其声明的文件中“可见”

(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 = 15a = 15, sa = 20a = 15, sa = 25a = 15, sa = 30a = 15, sa = 35a = 15, sa = 40a = 15, sa = 45a = 15, sa = 50a = 15, sa = 55a = 15, sa = 60

这对于函数需要在调用之间保持一些状态并且你不想使用全局变量的情况很有用。但是,要注意,这个特性应该非常谨慎地使用——它会让你的代码不是线程安全的,更难理解。

(2)被广泛用作“权限改造”特性。如果你有一个实现某些功能的. c文件,它通常只向用户公开几个“公共”功能。其剩余的功能应该设置为static,这样用户就无法访问它们。这是封装,一个很好的做法。

引用维基百科

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

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

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

来自wikipedia:

在C语言中,静态用于全局变量和函数来设置它们的作用域到包含文件。在局部变量中,静态用于将变量存储在静态分配的内存中,而不是自动分配的内存中。虽然该语言不规定任何一种内存的实现,静态分配的内存通常在编译时保留在程序的数据段中,而自动分配的内存通常实现为瞬态调用堆栈。

如果您在函数静态中声明变量,其值将不会存储在函数调用堆栈上,并且在您再次调用该函数时仍然可用。

如果您声明一个全局变量静态,它的作用域将被限制在您声明它的文件内。这比可以在整个程序中读取和修改的常规全局稍微安全一些。

static表示不同上下文中的不同事物。

  1. 您可以在C函数中声明一个静态变量。该变量仅在函数中可见,但它的行为像全局变量,因为它只初始化一次并保留其值。在这个例子中,每次调用foo()时,它都会打印一个递增的数字。静态变量只初始化一次。

    void foo (){static int i = 0;printf("%d", i); i++}
  2. Another use of static is when you implement a function or global variable in a .c file but don't want its symbol to be visible outside of the .obj generated by the file. e.g.

    static void foo() { ... }

简短的回答这要看情况。

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

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

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

这取决于:

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库中,所有“私有”函数都是静态的,而大多数“公共”函数不是。

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

在任何其他范围内,它声明一个对象,该对象将在进入特定范围的不同时间之间保留其值。例如,如果一个int在过程中被删除:

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

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

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

static int my_variable;

那么这个变量只能从这个文件中看到。该变量不能导出到其他任何地方。

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

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

多文件变量范围示例

在这里,我说明了静态如何影响多个文件中函数定义的范围。

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

github上游

编译并运行:

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

输出:

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

口译

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

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

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

标准怎么说呢

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

6.2.2/3“标识符的链接”说static意味着internal linkage

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

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 Name5: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    4 si10: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    4 i

所以绑定是它们之间唯一重要的区别。Value只是它们在.bss部分的偏移量,所以我们希望它有所不同。

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

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

这使得它成为表示static的完美选择。

没有静态的变量是STB_GLOBAL,规范说:

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

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

如果我们用-O3加速优化,si符号将完全从符号表中删除:无论如何它都不能从外部使用。待办事项没有优化时,为什么要在符号表中保留静态变量?它们可以用于任何事情吗?也许用于调试。

另见

C++匿名命名空间

在C++,您可能希望使用匿名命名空间而不是静态命名空间,这可以实现类似的效果,但会进一步隐藏类型定义:未命名/匿名命名空间与静态函数

这里还有一个用法没有介绍,那就是作为数组类型声明的一部分作为函数的参数:

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

在这种情况下,这指定传递给此函数的参数必须是char类型的数组,其中至少有10个元素。有关更多信息,请参阅我的问题这里

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

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

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

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

void func(void) {static int count; // If you don't declare its value, it is initialized with zeroprintf("%d, ", count);++count;}
int main(void) {while(true) {func();}return 0;}

输出:

0,1,2,3,4,5,…

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

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

例如:

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

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

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

var的值不会在函数调用之间被销毁。

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

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

关于静态的另一个信息:如果变量是全局和静态的,它具有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_(编程)
  2. http://en.wikipedia.org/wiki/Call_stack

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

简而言之,静态一词的含义为两个

  1. 静态是两个存储类之一(另一个是自动)。静态对象在两次调用之间保持其值。在所有块之外声明的对象始终是静态的,不能自动。
  2. 但是,当static关键词(非常强调它被用于code as a keyword)与声明一起使用,它为该对象提供内部链接,因此它只能在该翻译单元中使用。但是如果关键字在函数中使用,它会更改对象的储存类别(无论如何,对象只能在该函数中可见)。与静态相反的是extern关键字,它为对象提供外部链接。

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

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

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

  • 将“静态”应用于项目强制该项目具有两个属性:(a)它在当前范围之外不可见;(b)它是持久的。

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

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

将“静态”应用于函数就像将其应用于全局变量一样——代码必须是持久的(至少在语言内部),因此只能更改可见性。

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

有两种情况:

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

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

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

上面的程序将给我们这个输出:

First Counter Output = 1Second Counter Output = 1

因为一旦我们调用该函数,它就会初始化count = 0。当我们执行counterFunction时,它将销毁计数变量。

静态变量即使在超出其作用域后也具有保值的属性!因此,静态变量在其先前的作用域中保留其先前的值,并且不会在新作用域中再次初始化。

看看这个例子-静态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]

这些是我发现的主要问题,上面没有为新手解释!

分享我在这一点上学到的东西。

在C#中,0是声明说明符,分为三类:

  • 存储类:有四个类:自动,static,extern和注册。
  • 类型限定符:喜欢关键字:const、易失性等。
  • 类型说明符:喜欢关键字:ull、char、短、int等。

所以static存储类。它将确定C程序中每个变量的以下三个属性。

  • 存储时长:表示为变量分配内存和释放内存的时间。只要程序运行,存储持续时间为static的变量就会停留在相同的内存位置。
  • 范围:表示程序文本中可以访问变量的部分。静态变量的文件范围而不是块范围
  • 联系:表示变量可以被程序的不同部分(或文件)共享的程度。如果一个静态变量在块内声明,那么它有没有联系。如果一个静态变量在块外声明,那么它有内部链接。内部链接使它可以在单个文件中访问。

static储存类别对变量有不同的影响,这取决于它是在块外还是块内声明的。需要逐个考虑。