我在C代码的不同地方看到过静态这个词;这是否像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("");
}

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语言中,static有两个含义,这取决于它的使用范围。在全局作用域中,当在文件级声明对象时,这意味着该对象仅在该文件中可见。

在任何其他作用域,它声明一个对象,该对象将在输入特定作用域的不同时间之间保留其值。例如,如果一个int在一个过程中被delcare:

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

   i++;
}

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

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

void func(void) {
    static int count; // If you don't declare its value, it is initialized with zero
    printf("%d, ", count);
    ++count;
}

int main(void) {
    while(true) {
        func();
    }
    return 0;
}

输出:

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

人们总是说C语言中的“static”有两层含义。我提供了另一种看待它的方式,赋予它一个单一的含义:

对一个项目应用“static”会迫使该项目具有两个属性:(a)在当前范围外不可见;(b)它是持久的。

它似乎有两种含义的原因是,在C语言中,每个“static”可能应用的项都已经具有这两个属性之一,所以似乎这种特殊用法只涉及另一个。

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

将“static”应用于函数就像应用于全局变量一样——代码必须是持久的(至少在语言中),所以只能改变可见性。

注意:这些注释只适用于C。在c++中,对类方法应用'static'是真正赋予关键字不同的含义。C99数组参数扩展也是如此。

多文件变量范围示例

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

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.静态函数

有2种情况:

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

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