【问题标题】:Static variable inside of a function in CC中函数内部的静态变量
【发布时间】:2011-06-29 08:25:06
【问题描述】:

会打印出什么? 6 6 还是 6 7?为什么?

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

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

【问题讨论】:

  • 有什么问题要尝试?
  • 您是否尝试输入此内容并亲自查看?
  • 我想知道为什么。
  • @Vadiklk 所以要问以“为什么”开头的问题
  • ideone.com/t9Bbe 你会期待什么?结果不符合你的预期吗?你为什么期待你的结果?

标签: c static


【解决方案1】:

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

变量的作用域是可以看到变量名的地方。这里,x 仅在函数 foo() 内可见。

变量的生命周期是它存在的时间段。如果x 没有定义关键字static,则生命周期将从进入foo() 到从foo() 返回;所以它会在每次调用时重新初始化为 5。

关键字static的作用是将变量的生命周期延长到程序的生命周期;例如初始化只发生一次,然后变量保留它的值——不管它变成了什么——在所有未来对foo()的调用中。

【讨论】:

  • @devanl,是的,我们是。
  • 简单而合乎逻辑:)
  • 在什么情况下我们需要在函数中将变量声明为静态变量?只是想知道我以前没有使用过这个吗?
  • 如果我声明一个静态变量,然后像这样在单独的行上初始化会发生什么:static int a;a = 0; 在这种情况下,a 每次都会重置为 0。对吗?
  • @LakshyaGoyal 您的第二行是声明,而不是初始化。所以是的,你是对的,a 每次都为零(在第二行运行后立即)。当然,这可能会否定使用静态变量的好处。
【解决方案2】:

输出: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;
}

【讨论】:

    【解决方案3】:

    这与拥有以下程序相同:

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

    static 关键字在该程序中所做的只是告诉编译器(本质上)“嘿,我这里有一个变量,我不希望其他人访问,不要告诉其他人它存在”。

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

    希望对你有帮助

    【讨论】:

    • 嗯,实际上不一样。 X 上仍然存在范围问题。在此示例中,您可以在 main 中使用 x它是全球性的。在原始示例中,x 对 foo 是本地的,仅在该块内可见,通常更可取:如果 foo 存在以可预测和可见的方式维护 x,然后让其他人戳它一般是危险的。作为将其保持在foo() 范围内的另一个好处,它还保持foo()的便携性。
    • @user2149140 '不要告诉任何人这存在于这个函数之外,它应该只能在这个函数内部访问'
    • 虽然您已经解决了由于声明变量的位置而导致的范围问题,但将静态描述为影响范围而不是生命周期似乎不正确。
    • @Chameleon 该问题被标记为c,因此在这种情况下,您的示例在全球范围内是非法的。 (C 需要全局变量的常量初始化器,C++ 不需要)。
    【解决方案4】:

    6 7

    编译器安排每次进入函数时不进行静态变量初始化

    【讨论】:

      【解决方案5】:

      输出:6,7

      原因

      x 的声明在 foo 内部,但 x=5 初始化发生在 foo 外部!

      这里我们需要了解的是

      static int x = 5;
      

      不一样

      static int x;
      x = 5;
      

      其他回答都用到了这里的重要词,作用域和生命周期,并指出x的作用域是从它在函数foo中的声明点到函数foo的结尾。例如,我通过将声明移到函数末尾进行检查,这使得 xx++; 语句中未声明。

      因此,声明的static int x(范围)部分实际上适用于您阅读它的地方,INSIDE函数的某处,并且仅从那里开始,而不是函数内部的上方。

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

      我在其中一个 cmets 中读到:“另外,这并没有解决真正令人困惑的部分,即初始化程序在后续调用中被跳过。” 它被跳过所有通话。变量的初始化在函数代码之外。

      无论是否调用 foo ,理论上都会设置 5 的值,尽管如果你不在任何地方调用它,编译器可能会优化掉该函数。值 5 应该在 foo 被调用之前存在于变量中。

      foo 内部,语句static int x = 5; 根本不可能生成任何代码。

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 说这就像一个“全局”变量然后说除了你不能访问它是矛盾的。全球意味着在任何地方都可以访问。在这种情况下,静态 INSIDE 函数在任何地方都无法访问。正如其他人所指出的,OP 中的问题是关于范围和生命周期的。请不要混淆人们使用术语“全局”并在变量范围上误导他们。
        • @ChuckB:正确。修复。嗯,已经6年了。我之前的回答有6年前的感觉!
        【解决方案7】:

        让我们看看Wikipedia article on Static Variables...

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

        【讨论】:

        • 太糟糕了! “在函数中声明为静态的变量是静态分配的”——它什么也不解释,除非你已经知道它的含义!
        • @Blank:嗯,这就是我认为第二句话的用途。虽然我猜你是对的,但应该措辞更好。
        • 另外,这并没有解决真正令人困惑的部分,即在后续调用中会跳过初始化程序。
        • 静态分配意味着没有栈,也没有堆。
        【解决方案8】:

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

        【讨论】:

        • 你确定静态是在函数调用之前初始化的,而不是在第一次调用函数时初始化的?
        • @JessePepper:至少如果记忆有用,这取决于您是在谈论 C++98/03 还是 C++11。在 C++98/03 中,我相信它如上所述。在 C++11 中,线程基本上不可能做到这一点,所以初始化是在第一次进入函数时完成的。
        • 我认为你错了。我认为即使在 C++11 之前,它也只是在调用函数时才被初始化。这对于静态初始化依赖问题的通用解决方案很重要。
        【解决方案9】:

        瓦迪克,

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

        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(函数激活记录)。

        为了更多的理解,做上面的例子没有“静态”,让你知道输出是什么。这使您了解这两者之间的区别。

        谢谢 贾维德

        【讨论】:

          【解决方案10】:

          你会得到 6 7 打印出来,这很容易测试,原因如下:当第一次调用 foo 时,静态变量 x 被初始化为 5。然后它递增到 6 并打印出来。

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

          【讨论】:

            【解决方案11】:
            6 7
            

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

            【讨论】:

              【解决方案12】:

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

              【讨论】:

                【解决方案13】:

                至少在 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' 时,这不是必需的,并且可以在程序加载时使用编译器在输出汇编代码中存储的值来初始化变量。

                【讨论】:

                  猜你喜欢
                  • 2016-08-30
                  • 1970-01-01
                  • 2016-07-26
                  • 1970-01-01
                  • 2020-08-04
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多