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


当前回答

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

简而言之,“static”一词有两层含义:

静态是两个存储类之一(另一个是 自动)。静态对象在调用之间保持其值。在所有块外声明的对象总是静态的,不能自动的。 但是,当静态关键字(强调它被用于 代码作为关键字)与声明一起使用,它提供了该对象的内部链接,因此它只能在该翻译单元中使用。但是如果在函数中使用关键字,它将改变对象的存储类(无论如何,对象只在该函数中可见)。与static相对的是extern关键字,它提供对象外部链接。

Peter Van Der Linden在“Expert C Programming”中给出了两个含义:

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

其他回答

静态变量有一个特性,即使在它们超出作用域后也能保存它们的值!因此,静态变量在以前的作用域中保留它们以前的值,而在新的作用域中不再初始化。

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

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

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

这将输出:12

As 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)

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

函数中的静态变量在调用之间保持其值。 静态全局变量或函数只能在声明它的文件中“看到”

(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文件,它通常只向用户公开几个“公共”函数。其余的函数应该是静态的,这样用户就不能访问它们。这就是封装,一个很好的实践。

引用维基百科:

在C语言中,是静态的 与全局变量和 函数将其作用域设置为 包含文件。在局部变量中, Static用于存储变量 在静态分配的内存中 而不是自动分配 内存。而语言却没有 指定任意一种方法的实现 内存类型,静态分配 内存通常保留在数据中 编译时的程序段 时间,当自动 已分配内存正常 实现为一个瞬态调用堆栈。

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

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

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段中。

关于静态的另一个信息:如果一个变量是全局的并且是静态的,那么它具有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的引用。

引用:

http://en.wikipedia.org/wiki/Translation_unit_(编程) http://en.wikipedia.org/wiki/Call_stack

多文件变量范围示例

在这里,我将说明静态如何跨多个文件影响函数定义的作用域。

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("");
}

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.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只有一个共享变量

通常,作用域越小越好,所以如果可以,总是将变量声明为静态的。

在C语言编程中,文件经常被用来表示“类”,静态变量表示类的私有静态成员。

这是什么标准啊

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

6.2.2/3“标识符的关联”说静态意味着内部关联:

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

6.2.2/2表示内部链接的行为就像我们的例子:

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

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

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在ELF规范http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html:上有文档

局部符号在包含其定义的目标文件之外是不可见的。相同名称的局部符号可以存在于多个文件中而互不干扰

这使得它成为代表静态的完美选择。

不带static的变量是STB_GLOBAL,规范说:

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

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

如果我们使用-O3进行优化,si符号将完全从符号表中删除:它无论如何都不能从外部使用。当没有优化时,为什么要在符号表上保持静态变量?它们有什么用途吗?也许用于调试。

另请参阅

类似于静态函数:https://stackoverflow.com/a/30319812/895245 比较static和extern,它做“相反”:我如何使用extern在源文件之间共享变量?

C+匿名namespaces

在c++中,您可能希望使用匿名名称空间而不是静态名称空间,这可以达到类似的效果,但进一步隐藏了类型定义:未命名/匿名名称空间vs.静态函数

简单的回答……视情况而定。

静态定义的局部变量不会在函数调用之间丢失它们的值。换句话说,它们是全局变量,但作用域是定义它们的局部函数。 静态全局变量在定义它们的C文件之外是不可见的。 静态函数在定义它们的C文件之外是不可见的。