C中的内部静态变量,你会使用它们吗?

在C中,您可以拥有可在文件中的每个位置查看的外部静态变量,而内部静态变量仅在函数中可见但是持久

例如:

#include  void foo_bar( void ) { static counter = 0; printf("counter is %d\n", counter); counter++; } int main( void ) { foo_bar(); foo_bar(); foo_bar(); return 0; } 

输出将是

 counter is 0 counter is 1 counter is 2 

我的问题是你为什么要使用内部静态变量? 如果你不希望你的静态变量在文件的其余部分可见,那么该函数真的不应该在它自己的文件中吗?

这种混淆通常是因为static关键字有两个目的。

在文件级别使用时,它控制其对象在编译单元外的可见性 ,而不是对象的持续时间 (可见性和持续时间是我在教育会话期间使用的外行人的术语,ISO标准使用您最终可能想要学习的不同术语,但我发现他们混淆了大多数初学者)。

在文件级创建的对象已经根据它们处于文件级别的事实来确定它们的持续时间。 然后, static关键字使它们对链接器不可见。

在函数内部使用时,它控制持续时间 ,而不是可见性 。 可见性已经确定,因为它在函数内部 – 在函数外部看不到它。 在这种情况下, static关键字使对象与文件级对象同时创建。

注意,从技术上讲,函数级静态可能不一定会出现,直到首次调用该函数(这对于C ++及其构造函数可能有意义),但我曾经使用的每个C实现都创建了它的函数级静态。时间作为文件级对象。

此外,虽然我使用的是“对象”这个词,但我并不是指C ++对象的意思(因为这是一个C问题)。 这只是因为static可以应用于文件级别的变量或函数,我需要一个无所不包的词来描述它。

函数级静态仍然使用相当多 – 如果不能满足它们,它们可能会导致multithreading程序出现问题,但如果您知道自己在做什么(或者您没有进行线程处理),它们是最好的方法保持跨多个函数调用的状态,同时仍然提供封装。

即使使用线程,也可以在函数中执行一些技巧(例如在函数中分配特定线程的数据),以使其可行,而不会不必要地暴露函数内部。

我能想到的唯一其他选择是全局变量,每次都将“状态变量”传递给函数。

在这两种情况下,您将函数的内部工作方式暴露给它的客户端,并使函数依赖于客户端的良好行为(总是一个冒险的假设)。

它们用于实现像strtok这样的工具,它们会导致重入问题……

在使用此工具愚弄之前要仔细考虑,但有时候它们是合适的。

例如,在C ++中,它被用作获得单身人士的一种方式

 SingletonObject& getInstance() { static SingletonObject o; return o; } 

用于解决初始化顺序问题(虽然它不是线程安全的)。

广告“该function不应该在自己的文件中”

当然不是,那是胡说八道。 编程语言的许多重点是促进隔离,因此重用代码(局部变量,过程,结构等都这样做),这只是另一种方法。

BTW,正如其他人所指出的那样,几乎所有针对全局变量的论证也适用于静态变量,因为它们实际上是全局变量。 但是有很多情况下可以使用全局变量,人们也可以。

我觉得一次性,延迟,初始化很方便:

 int GetMagic() { static int magicV= -1; if(-1 == magicV) { //do expensive, one-time initialization magicV = {something here} } return magicV; } 

正如其他人所说,这是第一次调用时不是线程安全的,但有时你可以逃脱它:)

我认为人们通常远离内部静态变量。 我知道strtok()使用一个或类似的东西,因为这可能是C库中最讨厌的函数。

其他语言如C#甚至不支持它。 我认为过去的想法是在OO语言之前提供一些相似的封装(如果你可以称之为)。

可能在C中并不十分有用,但它们在C ++中用于保证命名空间范围静态的初始化。 在C和C ++中,它们在multithreading应用程序中的使用存在问题。

我不希望存在静态变量来强迫我将函数放入自己的文件中。 如果我有许多类似的函数,每个函数都有自己的静态计数器,我想把它放在一个文件中怎么办? 我们必须做出足够的决定来放置东西,而不需要再有一个约束。

静态变量的一些用例:

  • 您可以将它用于计数器,并且不会污染全局命名空间。
  • 您可以使用将值作为指针获取并返回内部静态的函数来保护变量。 这可以控制值的分配方式。 (当你只想获取值时使用NULL)

我从未听说过这个被称为“内部静态变量”的特定结构。 我想是一个合适的标签。

像任何构造一样,它必须以知识和负责任的方式使用。 您必须知道使用构造的后果。

它保持在最本地范围内声明的变量,而不必为该函数创建单独的文件。 它还会阻止全局变量声明。

例如 –

 char *GetTempFileName() { static int i; char *fileName = new char[1024]; memset(fileName, 0x00, sizeof(char) * 1024); sprintf(fileName, "Temp%.05d.tmp\n", ++i); return fileName; } 

VB.NET支持相同的构造。

 Public Function GetTempFileName() As String Static i As Integer = 0 i += 1 Return String.Format("Temp{0}", i.ToString("00000")) End Function 

其中一个分支就是这些函数不是可重入的,也不是线程安全的。

不再。 我已经在multithreading域中看到或听到了函数局部静态变量的结果,并且它并不漂亮。

在为微控制器编写代码时,我将使用本地静态变量来保存特定函数的子状态值。 例如,如果我有一个每当main()运行时调用的I2C处理程序,那么它将拥有自己的内部状态保存在静态局部变量中。 然后每次调用它时都会检查它处于什么状态并相应地处理I / O(将位推到输出引脚,拉一条线等)。

一个简单的用途是函数可以知道它被调用了多少次。

所有静态都是持久的,不受同步访问的保护,就像全局变量一样,因此必须谨慎谨慎地使用。 然而,它们肯定会派上用场,而且它们不一定值得在自己的文件中。

我在一个致命的错误记录function中使用了一个,它被修补到我的目标的错误中断向量,例如。 DIV被零。 调用此函数时,将禁用中断,因此线程不是问题。 但是,如果我在记录第一个错误的过程中引发了一个新的错误,就像错误字符串格式化程序崩溃一样,仍然会发生重新入侵。 在那种情况下,我必须采取更激烈的行动。

 void errorLog(...) { static int reentrant = 0; if(reentrant) { // We somehow caused an error while logging a previous error. // Bail out immediately! hardwareReset(); } // Leave ourselves a breadcrumb so we know we're already logging. reentrant = 1; // Format the error and put it in the log. .... // Error successfully logged, time to reset. hardwareReset(); } 

这种方法是针对非常不可能的事件进行检查,并且它是唯一安全的,因为中断被禁用。 但是,在嵌入式目标上,规则是“永不挂起”。 这种方法保证(在合理范围内)硬件最终会以某种方式重置。