在{}范围内声明变量时,它们是否仍会使用内存?

时间:2011-01-13 01:00:28

标签: c++ variables scope

在这个例子中,即使我永远不会使用变量WNDCLASSEX,x,y,cx,cy,当我进入消息循环时,它们仍会使用内存:

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow)
    {
     WNDCLASSEX wc;
     ...
     RegisterClassEx(&wc);

     const int cx = 640;
     const int cy = 480; 
     // center of the screen
     int x = (GetSystemMetrics(SM_CXSCREEN) - cx) / 2;
     int y = (GetSystemMetrics(SM_CXSCREEN) - cy) / 2;

     CreateWindow(..., x, y, cx, cy, ...);

     MSG msg;

     while (GetMessage(&msg, NULL, 0, 0) > 0)
     {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
     }
     return 0;
    }

但是我想知道,如果我把它们放在一个范围内,它们是否仍会在消息循环中使用内存? e.g。

int WINAPI WinMain (HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpArgs, int iCmdShow)
{
 {
  WNDCLASSEX wc;
  ...
  RegisterClassEx(&wc);

  const int cx = 640;
  const int cy = 480; 
  // center of the screen
  int x = (GetSystemMetrics(SM_CXSCREEN) - cx) / 2;
  int y = (GetSystemMetrics(SM_CXSCREEN) - cy) / 2;

  CreateWindow(..., x, y, cx, cy, ...);
 }

 MSG msg;

 while (GetMessage(&msg, NULL, 0, 0) > 0)
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
 return 0;
}

或者如果我将它们分成两个函数并在winmain中调用它们,例如

wnd_register(hInst);
wnd_create(hInst);

会阻止他们使用内存吗?

9 个答案:

答案 0 :(得分:6)

编译器在处理简单本地时有很大的余地,就像你在例子中一样。它们可能存在于堆栈中,它们可能仅作为机器代码中的直接值存在,或者它们可能只存在于寄存器中。堆栈空间通常在进入函数时分配。编译器将从堆栈指针中减去一些值,以便为所有本地生成空间。返回函数时,堆栈指针将恢复为原始值。这通常不会在退出不同范围块时完成。一旦不再使用变量,大多数编译器都会尝试积极地重用堆栈空间。在您的示例中,x和msg在堆栈上具有完全相同的地址是完全合法的,因为它们的使用是非重叠的。

我对this question的回答详细介绍了如何在堆栈上分配局部变量。

在您的示例中,常量cx和cy很可能在运行时没有内存支持它们,并且只是生成的代码中的立即值。 x和y很可能会存在于寄存器中,直到需要将它们压入堆栈以调用CreateWindow。 wc和msg几乎肯定会在堆栈中。

您不应该担心此级别的微优化 - 让编译器在其认为合适的情况下为局部变量分配空间。默认情况下,您有1 MB堆栈,这些变量消耗的数据量甚至不会记录为噪声。花时间担心更多有趣的问题。

答案 1 :(得分:3)

可能不是,但那是一个实现细节。它们虽然会被破坏(如果有任何东西可以进行析构函数调用)。系统是否以及何时恢复用于自动存储的内存未由标准指定。大多数人都会立即回复它。

答案 2 :(得分:1)

嗯,我不确定他们是否使用内存或标准对此有何看法。

我所知道的是,在内存块{}的末尾,将调用析构函数并且无法访问变量。这可能意味着,虽然它没有被释放,但至少它可以重复使用。

示例:

struct Foo {
    Foo(void) { std::cout << "Hi!"; }
    ~Foo(void) { std::cout << "Bye!"; }
};

int main(int argc, char * argv[])
{
    {
        Foo bar; // <- Prints Hi!
    } // <- Prints Bye!

    // Memory used by bar is now available.
}

编辑:感谢Tomalak Geret'kal;)

答案 3 :(得分:1)

一条神奇的建议:信任您的编译器。它优化。这很聪明。它比我们大多数人更好地优化。

如果您不确定,请使用分析器或在优化后检查编译器的汇编器输出。但请记住 - 平凡的优化是你应该在代码中做的事情,因为它没有意义,只会伤害你的代码的可读性。

某些变量(尤其是常量)不会在堆栈上使用任何内存,因为它们将映射到CPU寄存器或直接嵌入到汇编器指令中。

这意味着代码:

func(123+456*198*value);

int a = 123;
int b = 56;
int c = 400;
int d = b+c;
int e = d*198;
e *= value;
e += a;
func(e);

会编译成完全相同的东西(如果永远不再使用变量)。

说真的,不要打扰。如果你想优化,从算法的角度进行优化,而不是语法。

答案 4 :(得分:0)

哦,上帝不,程序运行时内存中有四个整数,真是浪费!

  1. 尝试一下,试图打印它们的简单消息框就足够了(我想)。
  2. 甚至不介意。

答案 5 :(得分:0)

{}中声明的变量将超出范围并丢失。事实上,如果你试图在块之外使用它们,你会得到编译错误:'x'是未声明的。但是,这很草率。只需为此代码创建一个函数,就像您在编辑中所说的那样。保持main()尽可能少的行是很好的编程习惯。

答案 6 :(得分:0)

他们不会。他们只会活到封闭的街区尽头。

答案 7 :(得分:0)

如果将它们放在函数中的嵌套作用域(第一个选项)中,那么当控件到达作用域的末尾时,变量将变得不可访问(如果直接使用它们则是编译错误,或者如果是运行时未定义的行为,则为保存指向其中一个的指针),运行它们的析构函数(如果有析构函数),并且实现可以重用堆栈帧中的存储空间。但是标准并没有要求它重用空间。

如果你将你的功能分成两部分(你的第二个选项)......就标准发型而言,没有区别!当函数返回时,变量变得不可访问,它们的析构函数被运行,并且实现可能重用它们的存储空间,但它不是必需的。并且已经已经认真实施 - 虽然不是C / C ++ - 但没有立即回收那些内存:最着名的是文章“Cheney on the M.T.A.

但是,我目前所知道的的所有 C / C ++ 实现 在函数返回时回收为函数的局部变量分配的内存。为嵌套局部范围变量回收内存更不确定。无论如何,正如其他几个人所提到的,在这种情况下,不值得担心几十个字节的堆栈空间。

就个人而言,我会将你的代码分解成两个函数,因为每个函数只执行一个任务。这对于长期维护来说通常更好。

答案 8 :(得分:0)

通常是的,如果变量完全驻留在堆栈上,那么它的空间将在封闭函数的整个持续时间内被占用。编译器通常计算函数变量可能占用的最大空间量,然后在首次输入函数时使函数立即分配所有空间。但是,在内部作用域的进入和退出时,仍然会调用构造函数和析构函数。可以重用一个范围中变量的空间来表示来自单独范围的变量。