C函数内部的静态变量


119

什么将被打印出来?6 6或6 7?又为什么呢

void foo()
{
    static int x = 5;
    x++;
    printf("%d", x);
}

int main()
{
    foo();
    foo();
    return 0;
}

54
有什么问题要尝试?
安德鲁

12
您是否尝试过输入并自己查看?
wilhelmtell

20
我想了解原因。
Vadiklk 2011年

7
@Vadiklk,所以提出以“为什么”开头的问题
Andrey

1
ideone.com/t9Bbe您期望什么?结果不符合您的期望吗?你为什么期望你的结果?
eckes,2011年

Answers:


186

这里有两个问题,生存期和范围。

变量的范围是可以看到变量名称的位置。在这里,x仅在函数foo()中可见。

变量的生存期是变量存在的时间。如果x是在没有关键字static的情况下定义的,则生存期将是从foo()的条目到foo()的返回;因此每次调用都会将其重新初始化为5。

关键字static的作用是将变量的生存期延长到程序的生存期;例如,初始化仅发生一次,然后该变量将在以后对foo()的所有调用中保留其值-无论它变为何种值。


15
@devanl,是的。
Orion elenzil 2016年

1
简单和逻辑:)
Dimitar Vukman

在什么情况下我们需要在函数内部将变量声明为静态?,只是好奇地知道,因为我以前没有使用过此方法?
Akay

我会说谢谢,但这都是在页面的最顶端回答的。人们不仅仅运行自己的代码,这使我发笑。xD
水坑

这个答案是错误的。当您考虑递归函数时,此处描述的定义不能解释其行为!
菲利普·库林

52

输出:6 7

原因:静态变量仅初始化一次(不同于自动变量),并且在运行时将忽略静态变量的进一步定义。如果未手动初始化,则将自动由值0初始化。所以,

void foo() {
    static int x = 5; // assigns value of 5 only once
    x++;
    printf("%d", x);
}

int main() {
    foo(); // x = 6
    foo(); // x = 7
    return 0;
}


10

与具有以下程序相同:

static int x = 5;

void foo()
{
    x++;
    printf("%d", x);
}

int main()
{
     foo();
     foo();
     return 0;
}

static关键字在该程序中所做的全部工作是(实际上)告诉编译器“嘿,我在这里有一个变量,我不希望其他任何人访问,也不告诉其他任何人它存在”。

在方法内部,static关键字告诉编译器与上面相同,但是,“不要告诉任何人此函数存在于此函数之外,只能在此函数内部访问”。

我希望这有帮助


13
好吧,实际上是不一样的。在X上仍然存在范围问题。在此示例中,您可以x在main中进行戳戳和调整。这是全球性的。在原始示例中x,本地是foo的唯一对象,只有在该块内才可见,这通常是可取的:如果foo存在以x可预测和可见的方式进行维护,那么让其他人戳它通常是危险的。将其保留在范围内的另一个好处是foo() 它也保持foo()可移植性。
user2149140 2014年

2
@ user2149140'不要告诉任何人此函数之外,它只能在此函数内部访问'
DCShannon 2015年

3
虽然已经解决了由于声明变量的位置而引起的范围问题,但是将static描述为影响范围而不是生存期的描述似乎是不正确的。
DCShannon

1
@Chameleon该问题被标记为c,因此在这种情况下,您的示例在全球范围内都是非法的。(C需要用于全局变量的常量初始化器,C ++不需要)。
理查德·罗斯三世

5

只要您的程序运行,函数内的静态变量就有生命周期。不会在每次调用函数时分配它,而在函数返回时将释放它。


说这就像一个“全局”变量,然后说除了不能访问它外,这是矛盾的。全球意味着可以在任何地方访问。在这种情况下,使用静态INSIDE函数无法在任何地方访问。正如其他人所指出的,OP中的问题与范围和生存期有关。请不要混淆使用“全局”一词,并在变量的范围上误导他们。
ChuckB

@ChuckB:正确。解决它。好了,已经有六年了。我以前的回答是6年前的感觉!
Donotalo

5

输出:6.7

原因

的声明在x内部,foo但是x=5初始化在外部进行foo

我们在这里需要了解的是

static int x = 5;

与...不同

static int x;
x = 5;

其他答案在这里使用了重要的词,作用域和生存期,并指出的作用域x从其在函数foo中的声明到函数结束foo。例如,我通过将声明移至函数末尾进行检查,从而使xx++;声明未声明。

因此static int x,该语句的(作用域)部分实际上适用于您在函数内部的某个地方阅读的地方,并且仅从那里开始,而不是函数内部的上方。

但是,x = 5语句的(生命周期)部分是变量的初始化,并且作为程序加载的一部分发生在函数的OUTSIDE之外。变量x带有5程序加载时的值。

我在其中的一条评论中读到了这句话:“ 而且,这并没有解决真正令人困惑的部分,这是在后续调用中跳过初始化程序的事实。 ”在所有调用中都将跳过它。变量的初始化不在功能代码的范围内。

理论上,无论是否调用foo都将设置5的值,尽管如果您未在任何地方调用foo,编译器可能会对其进行优化。在调用foo之前,值5应该在变量中。

在的内部foo,该语句static int x = 5;根本不会生成任何代码。

x当我将函数foo放入我的程序时,我发现该地址使用了,然后(正确)猜测如果再次运行该程序将使用相同的位置。下面的部分屏幕截图显示了x该值5甚至在第一次调用之前就具有foo

第一次调用foo之前的断点


2

输出将是6 7。静态变量(无论是否在函数内部)在执行该转换单元中的任何函数之前仅初始化一次。之后,它将保留其值直到被修改。


1
您确定静态函数是在调用函数之前而不是在第一次调用函数时初始化的吗?
杰西·

@JessePepper:至少如果有内存可用,这取决于您是在谈论C ++ 98/03还是C ++ 11。我相信在C ++ 98/03中,如上所述。在C ++ 11中,线程使这基本上不可能实现,因此初始化是在第一次输入该函数时完成的。
杰里·科芬

2
我认为您实际上是错的。我认为即使在C ++ 11之前,也只能在调用函数时进行初始化。这对于静态初始化依赖性问题的通用解决方案很重要。
杰西·派珀

2

瓦迪克

为什么...?原因是静态变量仅初始化一次,并在整个程序中保持其值。意味着,可以在函数调用之间使用静态变量。也可以用来计算“一个函数被调用了多少次”

main()
{
   static int var = 5;
   printf("%d ",var--);
   if(var)
      main();
} 

答案是5 4 3 2 1,而不是您期望的5 5 5 5 5 5 ....(无限循环)。再次,原因是静态变量被初始化了一次,下次调用main()时,它不会被初始化为5,因为它已经在程序中初始化了。因此我们可以更改该值,但不能重新初始化。多数民众赞成在静态变量的工作原理。

或者,您也可以考虑按存储方式:将静态变量存储在程序的数据段中,并将存储在数据段中的变量初始化一次。并在初始化之前将它们保存在BSS部分中。

反过来,自动(本地)变量存储在堆栈中,并且在为此创建新的FAR(功能激活记录)时,始终将堆栈中的所有变量重新初始化。

可以,为了更好地理解,请在没有“ static”的情况下执行上述示例,并让您知道输出是什么。这使您了解这两者之间的区别。

谢谢贾维德


1

让我们看一下有关静态变量Wikipedia文章 ...

静态局部变量:在函数内部声明为静态的变量被静态分配,同时具有与自动局部变量相同的作用域。因此,当再次调用该函数时,该函数在一次调用期间放入其静态局部变量的任何值仍将存在。


5
这太可怕了!“在函数内部声明为静态的变量是静态分配的”-它没有任何作用,除非您已经知道它的含义!

@空白:恩,这就是我认为第二句话的目的。尽管我猜你是对的,但措辞应该更好。
安德鲁·怀特

而且,这并没有解决真正令人困惑的部分,这是在后续调用中跳过初始化程序的事实。
Tom Auger

静态分配意味着没有堆栈,也没有堆。
变色龙

1

正如容易测试的那样,您将得到6 7的打印结果,原因如下:foo第一次调用时,静态变量x初始化为5。然后将其递增到6并打印。

现在,下一个呼叫foo。程序将跳过静态变量初始化,而是使用上次分配给x的值6。执行将正常进行,为您提供值7。


1
6 7

x是一个全局变量,仅在foo()中可见。5是其初始值,存储在代码的.data部分中。任何后续修改都会覆盖先前的值。在函数体中没有生成分配代码。


1

6和7因为静态变量仅初始化一次,所以5 ++在第一次调用时变成6,在第二次调用时6 ++变成7注意-当第二次调用发生时,因为x是静态变量,所以x的值是6而不是5。


0

至少在C ++ 11中,当用于初始化局部静态变量的表达式不是“ constexpr”(不能由编译器求值)时,则必须在首次调用该函数期间进行初始化。最简单的示例是直接使用参数初始化本地静态变量。因此,编译器必须发出代码以猜测该调用是否是第一个调用,而这又需要一个局部布尔变量。我已经编译了这样的示例,并通过查看汇编代码来检查这是否正确。这个例子可以像这样:

void f( int p )
{
  static const int first_p = p ;
  cout << "first p == " << p << endl ;
}

void main()
{
   f(1); f(2); f(3);
}

当然,当表达式为'constexpr'时,则不需要这样做,并且可以使用编译器在输出汇编代码中存储的值在程序加载时初始化变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.