我在C代码中看到过static
这个词用在不同的地方;这就像C#中的静态函数/类(其中实现是跨对象共享的)?
如果你是新手,(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与全局变量和函数一起使用,以将其范围设置为包含文件。在局部变量中,static用于将变量存储在静态分配的内存中,而不是自动分配的内存中。虽然该语言没有规定任何类型的存储器的实现,但静态分配的存储器通常在编译时保留在程序的数据段中,而自动分配的存储器通常实现为瞬态调用栈。
并回答你的第二个问题,它不像在C#中。
但是,在C ++中,static
还用于定义类属性(在同一个类的所有对象之间共享)和方法。在C中没有类,所以这个功能是无关紧要的。
我讨厌回答一个老问题,但我认为没有人提到K&R如何在“C编程语言”的A4.1节中解释它。
简而言之,static这个词有两个含义:
extern
关键字(强调它在代码中用作关键字)与声明一起使用时,它会为该对象提供内部链接,因此它只能在该转换单元中使用。但是如果关键字在函数中使用,它会更改对象的存储类(该对象只能在该函数中可见)。与static相反的是void procedure(void)
{
static int i = 0;
i++;
}
关键字,它为对象提供外部链接。Peter Van Der Linden在“专家C编程”中给出了这两个含义:
在C中,static有两个含义,具体取决于其使用范围。在全局范围内,当在文件级别声明对象时,意味着该对象仅在该文件中可见。
在任何其他范围内,它声明一个对象,该对象将在输入特定范围的不同时间之间保留其值。例如,如果在过程中对del进行了delcared:
mytest.c
在第一次调用过程时,'i'的值初始化为零,并且每次调用过程时都会保留该值。如果'i'被打印,它将输出0,1,2,3 ......的序列...
重要的是要注意函数中的静态变量在第一次进入该函数时初始化,并且即使在它们的调用完成之后仍然存在;在递归函数的情况下,静态变量只被初始化一次,并且在所有递归调用之后仍然存在,甚至在函数调用完成之后也是如此。
如果变量已在函数外部创建,则意味着程序员只能使用已声明变量的源文件中的变量。
如果你在static int my_variable;
文件中声明:
*.c
那么这个变量只能从这个文件中看到。该变量无法在其他任何地方导出。
如果在函数内部声明,则每次调用函数时,变量的值都将保持其值。
无法从文件外部导出静态函数。因此,在void function()
{
static int var = 1;
var++;
printf("%d", var);
}
int main()
{
function(); // Call 1
function(); // Call 2
}
文件中,如果将它们声明为静态,则隐藏函数和变量。
C中的静态变量具有程序的生命周期。
如果在函数中定义,它们具有局部范围,即只能在这些函数内访问它们。函数调用之间保留静态变量的值。
例如:
var
在上面的程序中,var
存储在数据段中。它的生命周期是整个C程序。
在函数调用1之后,var
变为2.在函数调用2之后,var
变为3。
函数调用之间不会破坏var
的值。
如果var
介于非静态变量和局部变量之间,则它将存储在C程序的堆栈段中。由于函数的堆栈帧在函数返回后被销毁,因此static int x;
int main()
{
printf("Accessing in same file%d", x):
}
的值也被销毁。
初始化的静态变量存储在C程序的数据段中,而未初始化的静态变量存储在BSS段中。
关于static的另一个信息:如果变量是全局变量和静态变量,它具有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
现在尝试使用以下方法链接它们
http://en.wikipedia.org/wiki/Translation_unit_(programming)
它会产生链接器错误,因为x具有file1.c的文件范围,并且链接器将无法解析对file2.c中使用的变量x的引用。
参考文献:
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();
}
}
静态变量是一个可以在函数中使用的特殊变量,它在调用之间保存数据,并且不会在调用之间删除它。例如:
static
输出:
0, 1, 2, 3, 4, 5, ...
静态变量值在不同的函数调用之间持续存在,并且其范围仅限于本地块,静态var始终使用值0初始化
有2种情况:
(1)声明的局部变量static
:分配在数据段而不是堆栈中。当您再次调用该函数时,其值将保留。
(2)声明的全局变量或函数#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;
}
:不可见的外部编译单元(即链接期间符号表中的局部符号)。
在C编程中,static是一个保留关键字,它控制生命周期和可见性。如果我们在函数内声明一个变量为静态,那么它只能在整个函数中可见。在这种用法中,这个静态变量的生命周期将在函数调用时开始,并且在执行该函数后它将被销毁。你可以看到以下例子:
qazxswpoi
上面的程序会给我们这个输出: 第一计数器输出= 1 第二计数器输出= 1 因为只要我们调用函数它就会初始化count = 0.而当我们执行counterFunction时它会破坏count变量。
还有一个用途未在此处介绍,它作为数组类型声明的一部分作为函数的参数:
int someFunction(char arg[static 10])
{
...
}
在此上下文中,它指定传递给此函数的参数必须是char
类型的数组,其中至少包含10个元素。有关更多信息,请参阅我的问题here。
简短回答......这取决于。
多文件变量范围示例
这里我将说明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;
}
编译并运行:
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
:
如果对象或函数的文件范围标识符的声明包含静态的存储类说明符,则标识符具有内部链接。
和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为什么在没有优化时根据符号表保留静态变量?他们可以用于任何事情吗?也许是为了调试。
也可以看看
static
函数:https://stackoverflow.com/a/30319812/895245static
与extern
进行比较,How do I use extern to share variables between source files?做“相反”:Unnamed/anonymous namespaces vs. static functionsC ++匿名命名空间
在C ++中,您可能希望使用匿名命名空间而不是静态命名空间,这实现了类似的效果,但进一步隐藏了类型定义:int foo()
{
static int x;
return ++x;
}
这取决于:
static int foo()
{
}
该函数将返回1,2,3等等---变量不在堆栈中。
foo()
这意味着此函数仅在此文件中具有范围。所以a.c和b.c可以有不同的b.c
s,而foo不会暴露给共享对象。因此,如果您在a.c中定义了foo,则无法从static
或任何其他位置访问它。
在大多数C库中,所有“私有”函数都是静态的,而大多数“公共”函数则不是。
人们一直说C中的“静态”有两个含义。我提供了一种查看它的替代方法,它赋予它一个单一含义:
它似乎有两个含义的原因是,在C中,可以应用“静态”的每个项目都具有这两个属性中的一个,所以看起来好像这个特定用法只涉及另一个。
例如,考虑变量。在函数之外声明的变量已经具有持久性(在数据段中),因此应用'static'只能使它们在当前作用域(编译单元)之外不可见。相反,在函数内部声明的变量已经在当前作用域(函数)之外具有不可见性,因此应用“静态”只能使它们持久化。
将'static'应用于函数就像将其应用于全局变量一样 - 代码必须是持久的(至少在语言中),因此只能改变可见性。
注意:这些注释仅适用于C.在C ++中,将“static”应用于类方法确实赋予关键字不同的含义。类似地,对于C99数组参数扩展。
来自维基百科:
在C编程语言中,static与全局变量和函数一起使用,以将其范围设置为包含文件。在局部变量中,static用于将变量存储在静态分配的内存中,而不是自动分配的内存中。虽然该语言没有规定任何类型的存储器的实现,但静态分配的存储器通常在编译时保留在程序的数据段中,而自动分配的存储器通常实现为瞬态调用栈。
foo()
在不同的背景下意味着不同的东西。
void foo ()
{
static int i = 0;
printf("%d", i); i++
}
时,它将打印一个越来越多的数字。静态变量只初始化一次。
.obj
static void foo() { ... }
之外可见。例如
static
如果在函数static中声明一个变量,它的值将不会存储在函数调用堆栈中,并且在再次调用该函数时仍然可用。
如果声明一个全局变量static,其范围将限制在您声明它的文件中。这比常规全局更安全,可以在整个程序中读取和修改。