【问题标题】:C++: Static variable declarations in a functionC++:函数中的静态变量声明
【发布时间】:2011-07-08 09:55:03
【问题描述】:

静态变量存在于函数之外,至少就它们的内存而言(不是范围),对吧?但是我一直关心的一件事是,当我第二次调用该函数时会发生什么。例如:

f(){
    static char buffer[256*256];
    stuff(buffer);
}

当我第二次调用此函数时,从技术上讲,它不是第二次声明变量“缓冲区”吗?还是编译完所有内容后,它对静态变量(与普通变量相反)的工作方式是否不同?

...我有时希望有一个图表或一些 c++ 编译器通常会将代码转换成的东西(减去优化),这样我就不必用这样的小问题来打扰你们了,啊哈。提前谢谢!

编辑:我知道它是这样工作的,但我只是想知道为什么。这可能是一件令人麻木的简单事情......

【问题讨论】:

    标签: c++ function static-variables


    【解决方案1】:

    函数范围内的静态存储持续时间对象。

    这些对象是在首次使用时创建的。
    然后以创建的相反顺序销毁(与其他静态存储持续时间对象)。

    #include <iostream>
    
    class X
    {
        public:
            X(int x): m(x)      {std::cout << "X: " << m << " created\n"; }
            ~X()                {std::cout << "X: " << m << " destroyed\n";}
    
        private:
            int m;
    };
    
    
    static X    x1(1);
    
    int test()
    {
        std::cout << "Test: Start\n";
        static  X x3(3);
    
        std::cout << "Test: Finished\n";
        return 5;
    }
    
    
    int main()
    {
        std::cout << "Main: Start\n";
        X   x2(2);
    
        test();
    
        X   x4(4);
        std::cout << "Main: Finished\n";
    }
    

    现在试试看:(添加了 cmets)。 SSDO => 静态存储持续时间对象。

    g++ X.cpp
    ./a.out
    X: 1 created    // SSDO file scope.
    Main: Start
    X: 2 created
    Test: Start
    X: 3 created    // SSDO created on first use (Notice not destroyed)
    Test: Finished
    Test: Start     // Notice not created here.
    Test: Finished
    X: 4 created
    Main: Finished
    X: 4 destroyed
    X: 2 destroyed  // Main now really finished. after destroying local variables.
    X: 3 destroyed  // Destroy SSDO in reverse order of creation. (3 - 1)
    X: 1 destroyed
    

    【讨论】:

      【解决方案2】:

      不,您static 表示它超出了您的功能范围。和写的效果一样:

      static char buffer[256*256];
      
      f(){
          stuff(buffer);
      }
      

      除了缓冲区只在你的函数范围内可见,而且代码更具可读性。

      (注意:当 char 不是原始类型时,我的示例不适用 - 在这种情况下,它是在第一次“声明”时构造的)。

      【讨论】:

      • 好的,我必须记住这个范围规则!
      • 顺便说一句,这里有一个例子,说明变量 - 如果它是一个类 - 只有在你声明它时才会被构造:codepad.org/7LQpGnIn
      • ...哦!我从不使用类作为静态成员的原因正是这个,不知道如何正确初始化它。认为我不能指望构造函数,因为我不知道它什么时候会被调用。不必再担心这个了。谢谢你的例子!
      • 这是不正确的,static 不会影响在函数体中声明的变量的范围。声明为static 的变量仍然无法在函数外部访问,它的作用域不等同于在命名空间范围内声明变量;受影响的是存储持续时间。
      【解决方案3】:

      在此上下文中,static 表示该变量具有应用程序生命周期。它在main()函数进入之前分配,在main()返回后释放。此外,它的值在函数调用之间保留。将其视为仅在该函数内部可见的全局变量。

      【讨论】:

      • 好的,编译器会在其他所有内容开始运行之前分配所有内存。这就是我想知道的!我认为它在第一次调用该函数时创建了内存。
      • 不完全正确。它是在首次使用时分配的。然后当 main 退出时,所有静态存储持续时间对象都以创建的相反顺序被销毁。现在编译器可能会为这样的静态缓冲区预先分配空间作为优化,但从技术上讲,它在第一次使用之前并不存在。
      • @Martin:是的,我不确定,所以我去看了the standard。但是,它说(3.7.1.1):“这些对象的存储将持续到程序的持续时间(3.6.2、3.6.3)。” OTOH,3.6.2 描述了“非本地对象的初始化”,我还没有找到关于本地静态的部分。简而言之,我会听从您的专业知识。 :)
      • @MartinYork:从技术上讲,作为具有静态存储持续时间的 POD 变量,它确实存在于程序的生命周期中(甚至在第一次进入函数之前),并且它将被零初始化。这是一个学术观点,因为在控制通过函数中的声明之前无法引用变量。请参阅此处的讨论:stackoverflow.com/questions/4977290
      • @Charles:基于 3.7.1 的措辞(说存储(而不是变量))我可以在这里狡辩。但正如你所说的学术和最后我可能是错误的和羞辱。所以我谦虚地承认这一点:-)
      【解决方案4】:

      变量在你调用函数之前和之后都存在......它是静态的。

      【讨论】:

      • 第一次使用后才存在。
      【解决方案5】:

      这个例子可以说明:

      #include <iostream>
      using namespace std;
      
      void test() {
        static int i = 123;
        if (i == 123) {
          i = 321;
        }
        cout << i << endl;
      }
      
      int main(int arg, char **argv) {
        test();
        test();
        return 0;
      }
      

      输出是:

      321

      321

      所以“i”只在第一次遇到时才被初始化,可以这么说。但实际上它是在编译时为该函数分配的。之后,它只是作为变量在函数 test() 的范围内,但它是静态的,因此更改它也会在以后对 test() 的所有调用中更改它。

      【讨论】:

      • 因此,即使 i 在函数的一开始就被分配了 123,但在该函数中再也不会这样做了。这就是让我困惑的地方!所以我想就像另一个答案所说的那样,它在程序进入 main() 之前将此初始化移动到 123 的值?编辑:实际上,一起阅读所有答案,似乎它会这样做,除非它是一个对象。好的!这就是我需要知道的!谢谢!
      • 如果将 i 更改为一个类并从构造函数/析构函数中进行一些打印,您会看到它在第一次使用之前不存在(尽管编译器可能已经为它预先分配了空间) .
      • @Clairvoire:对于像这样的简单情况,编译器可能已经对其进行了优化,使其在编译时包含 123。但从技术上讲,直到第一次调用该函数时它才存在。第一次(也是第一次)对象将被初始化(像这样的 POD 将被分配类对象将调用它们的构造函数)。
      • @Martin:好的,这是让我感到困惑的主要事情。但现在我确切地知道它们何时被创建和销毁。这可能是我直到现在才宣布任何静态内容的主要原因,其中涉及的模棱两可。 (我实际上认为你必须小心并在函数中它自己的分支中初始化变量或类似的可怕的东西)。感谢您为我清理这一切!啊哈,我几乎希望我可以将其他答案的答案奖励给 cmets。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多