为什么和何时在C编程中使用静态结构?

52

我经常在需要修改的驱动程序代码中看到静态结构声明。

我尝试了解为什么会声明structs并进行此操作的动机。

请问有谁能帮助我理解这个问题吗?

5个回答

45
在C语言中,static关键字有不同的作用,取决于它所应用的上下文。
  • 当应用于函数内声明的变量时,该变量的值将在函数调用之间保留。
  • 当应用于函数外声明的变量或函数时,该变量或函数的可见性仅限于其声明的"翻译单元" - 即文件本身。对于变量来说,这归结为一种“局部可见的全局变量”。
这两种用法在相对低级别的代码(如驱动程序)中非常常见。
前者和后者应用于变量时都允许函数在调用之间保留状态概念,这可能非常有用,但当代码在任何并发使用的上下文中被使用时,这也可能导致各种严重问题,无论是多个线程还是多个调用者。如果您无法保证代码将由一个“用户”顺序调用,则可以在每次调用时传递一种由调用者维护的“上下文”结构。
后者应用于函数时,允许程序员使函数在模块外部不可见,并且它在某些编译器和某些架构下可能会更快,因为编译器知道它不必使变量/函数在模块外部可用 - 例如,允许函数内联。

3
小心谨慎?为什么?只有在需要具有静态存储期或内部链接的对象时才使用它们。其他所有情况都是虚假的陈述。 - Jens
1
@Jens 我也不是母语人士,我只是想以开玩笑的方式指出静态存储确实存在真正的潜在危险 - 希望你觉得我重新表述的更清楚了? - fvu
4
好的,谢谢。原始措辞可能会被初学C语言的程序员误解为“哦,静态变量必须小心使用,所以它们肯定有什么问题。我应该像goto和setjmp一样避免它们。” 它们有用处,并且甚至受到编程指南的鼓励,这些指南看重标识符的最小链接性。 - Jens
3
@Jens 真的,这就是为什么我欢迎这次机会来改善这段文字。 - fvu
1
@fvu:我喜欢改进后的文本。另一个关于函数内使用static变量的要点是,有时候定义一个参数或一组参数值来“重置”这些变量是很有用的。将所有对某些变量的访问限制在一个函数内可以更容易地理解它们,但如果在编写函数时没有提供重置它们的方法,那么如果以后需要多次“从头开始”运行该函数(在某些测试场景中可能很常见),添加重置功能可能会很困难。 - supercat

41

显然其他答案似乎都没有提到的一点是: static 还指定了对象的 存储期限,与 自动变量(局部变量)和 分配内存(由 malloc 等函数返回的内存)一起。

具有静态存储期的对象在 main() 函数开始之前进行初始化,可以使用指定的初始化程序进行初始化,如果没有指定,则像将 0 赋给它一样进行(对于结构体和数组,这适用于每个成员和递归操作)。

第二个属性 static 为标识符设置的是其 链接性,这是一个在链接时使用的概念,告诉链接器哪些标识符引用相同的对象。 static 关键字使标识符具有 内部连接性,这意味着它不能引用另一个翻译单元中相同名称的标识符。

至于我之前看过的所有松散的答案,需要严谨一点:静态变量无法在声明它的文件中的任何位置引用。它的作用域只在其声明(可以在函数定义之间)到源文件的结尾--甚至更小,到封闭块的结尾。


31

结构体 变量

对于像static struct S s;这样的结构体变量,已经在以下网址广为讨论:What does "static" mean in C?

结构体 定义:无影响:

static struct S { int i; int j; };

就是完全相同的意思:

struct S { int i; int j; };

因此永远不要使用它。如果您这样做,GCC 4.8会发出警告。

这是因为结构定义没有存储,并且不像变量和函数一样在目标文件中生成符号。只需尝试编译和反汇编:

struct S { int i; int j; };
int i;

随着:

gcc -c main.c
nm main.o

你会发现没有S符号,但有一个i符号。

编译器只是使用定义在编译时计算字段的偏移量。

这就是为什么结构体定义通常包含在头文件中的原因:即使多次包含,它们也不会生成多个单独的数据。

enum也是如此。

C++结构体定义:在C++11中已过时

C++11 N3337标准草案附录C 7.1.1:

更改:在C ++中,static或extern限定符只能应用于对象或函数的名称 在类型声明中使用这些限定符在C ++中是非法的。在C中,当用于类型声明时,这些限定符将被忽略。

另请参阅:https://dev59.com/AXVD5IYBdhLWcg3wWKPc#31201984


9
如果你将一个变量声明为 static,它只在该翻译单元中可见(如果是全局声明),或者在函数内部声明时保留其值以供调用使用。
在你的情况下,我猜测这是第一种情况。在这种情况下,可能程序员不希望从其他文件中看到该结构体。

1
这非常不精确。1)静态声明标识符的范围(“可见性”?)不是翻译单元,而是从声明后到封闭块的末尾,或者如果在文件作用域中声明,则为文件的末尾。2)链接是内部的。3)即使在文件作用域中,静态变量也会保留其值。 - Jens
是的,我知道。但我认为这些信息对于这个问题的观点来说并不重要。 - Mihai Maruseac
1
我认为在教学时,正确的术语和精确性是我们最能帮助学生的。C语言标识符有作用域、链接、存储期、命名空间和类型。我尽量不假设学生可能认为什么重要,因为我的判断可能会出错 :-) - Jens

7
structstatic 修饰符将结构的可见范围限制在当前翻译单元(即文件)内。
注意:本答案假定(如其他回答者所示)您的声明不在函数内部。

1
严谨地说:“(即文件)” 是错误的。作用域从声明之后开始。在此之前定义的函数不能引用后面声明的变量(无论是静态的还是外部的)。 - Jens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接